Skip to content

DRAFT / DO NOT MERGE — AIP-14b dispute system CI/audit gate validation only#3

Draft
DamirAGI wants to merge 18 commits into
mainfrom
feat/aip14b-dispute-system
Draft

DRAFT / DO NOT MERGE — AIP-14b dispute system CI/audit gate validation only#3
DamirAGI wants to merge 18 commits into
mainfrom
feat/aip14b-dispute-system

Conversation

@DamirAGI

Copy link
Copy Markdown
Collaborator

DRAFT — DO NOT MERGE. This PR exists solely to run the AIP-14b dispute-system CI/audit release-gate in the real GitHub-Actions PR environment. The CI triggers only on push:[main] / pull_request:[main], so a feature-branch push cannot exercise it — this draft is the reproducible, outward-facing control artifact. There is no merge intent.

Merge is BLOCKED until ALL of:

  • P0-7 — Nex-QA sign-off of the AIP-14 FINAL spec
  • Spec status reconciled Draft → Final
  • P5-5 — external audit complete, HIGH/CRITICAL = 0
  • P5-3 — shadow-mode run (broadcast-gated; needs the Sepolia testnet deploy)

Branch contents (5 commits)

  • 734a275 Phase 4 testnet-deploy machinery (P4-0..P4-7)
  • 2d3e857 Phase 4 deploy-script audit (broadcast detection + rotating pool ≥3)
  • 24e4edd on-chain prompt-CID governance (closes audit HIGH-1)
  • 79b76ea audit MED-3 + MED-5 (rotating-floor + monitoring ref)
  • f5c7d64 Phase 5 audit-readiness (P5-1/2/4) + INV-18 coverage

CI release-gate this PR validates (P5-2)

  • Forge unit + invariant + dispute suites (681 / 0 / 5-skip locally, --offline)
  • 23 audit PoCs (clean rebuild)
  • per-contract coverage floors (BondEscalation + CompositeMediator)
  • EIP-170 size ceiling — hardened parser (the "Extra data" json.load fix)
  • canonical ruling-encoding lint (INV-1)
  • Slither, gating on HIGH
  • invariant-coverage map: 0 UNMAPPED, fail-closed if the doc is absent

The off-chain evaluator service (Phase 3 + P5-6 unit-economics) lives in a separate local-only repo (services/dispute-evaluator, no remote yet) — out of scope for this PR; for the audit it is a local evidence bundle until a private repo is created.

🤖 Generated with Claude Code

DamirAGI and others added 18 commits June 22, 2026 12:56
…ositeMediator + BondEscalation

- Kernel: open DISPUTED->SETTLED/CANCELLED resolver-auth to approved+timelocked
  mediators via _isApprovedResolver (admin || mediator; G1 decision: no pauser);
  symmetric at _enforceAuthorization and _handleCancellation (mutation-proven).
- CompositeMediator.sol: thin ruling->kernel mapper (INV-1/2/22; G4 write-once
  init with deployer guard; ZERO_REMAINING_SENTINEL for drained-escrow disputes).
- BondEscalation.sol: Tier-0/1/2 (bond-doubling game + UMA OOV3 + EIP-712 2/3
  evaluator registry with overlap guard + timelocked governance); recovery paths
  stay open while paused (INV-9).
- Interfaces: IBondEscalation/IBondEscalationAdmin/ICompositeMediator/
  IOptimisticOracleV3 (vendored UMA) + DisputeTypes (AIRuling); foundry [invariant].
- Tests (120+): ResolverAuth, CompositeMediator, BondEscalation unit/adversarial/
  UMA/stateful-invariant/threat-model, EncodingCanonical (EIP-712 golden vector).
  Full suite: 626 passed / 0 failed / 1 skipped.

Refs: DISPUTE SYSTEM/PRD-dispute-system-implementation.md (M0->M1), AIP-14b v5.8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…+ 4 MEDIUM)

Post-implementation audit of the AIP-14b three-tier dispute system found
3 HIGH + 4 MEDIUM findings that the 626 unit/invariant tests missed
(emergent cross-contract coupling). Fixed surgically; kernel stays the sole
fund authority and INV-1 ruling encoding is unchanged. PoCs in test/audit/
are flipped to assert the fixed behavior.

HIGH
  F-1  Sub-MIN_FEE provider split bricks dispute finalize for up to 30d
       -> clamp fee to grossAmount (ACTPKernel)
  F-2  Kernel pause transitively bricks BondEscalation recovery (INV-9 defeated)
       -> resolveDisputeWhilePaused + _transitionState refactor;
          CompositeMediator routes DISPUTED exits through it
  F-3  UMA escalator captures the whole Tier-1 bond pool (free-rider theft)
       -> remove the erroneous pool credit (BondEscalation)

MEDIUM
  F-4  UMA callback OOG can revert settleAssertion, no graceful degrade
       -> retryMediatorResolution + defensive mark-needs-manual-resolve
  F-5  Uncapped initial Tier-1 bond enables cheap large-bond griefing
       -> cap initial bond at MAX_ESCALATION_BOND
  F-6  Clean UMA win silently degrades to forced 50/50 at 30d (no settle-bounty)
       -> settleUMAAssertion on-chain settle-bounty (PRD Option-1)
  F-7  Reputation (ERC-8004) leg untested in every dispute test
       -> dispute test with a wired AgentRegistry + fault-attribution asserts

Suite 651 passed / 0 failed / 1 skipped. Kernel 22,291 B. PoCs: DustSplitBrick,
KernelPauseBricksRecovery, UMABondTheft, UMASettleBountyAndDeferral,
DisputeMoneyPathCoverage, AdminPauseStrandProbe. Detail in
DISPUTE SYSTEM/PRODUCTION-READINESS-AUDIT.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ess)

Deterministic, court-free liveness backstop: once deadline + recoveryGrace elapses, anyone can drive a stalled IN_PROGRESS escrow to CANCELLED with a full refund to the requester. Closes the F-6 deadlock where a non-delivering provider permanently stranded the buyer's funds (no cancel/dispute/settle exit).

- MIN_DEADLINE + MIN_RECOVERY_GRACE constants; immutable recoveryGrace (6th constructor arg: 7 days mainnet, 1 hour testnet/local).
- recoverStalledInProgress(bytes32): nonReentrant, no whenNotPaused (fund-recovery precedent), no proof param, full refund of vault.remaining, CEI, StalledInProgressRecovered event, provider-at-fault reputation mark (C-2-guarded, same path as settlement).
- Option A: IN_PROGRESS->DELIVERED allowed through the grace window (strict < deadline+recoveryGrace, mutually exclusive with recovery's >=) so an honest provider that did the work can still deliver and be paid — closes steal-work.
- H-4 cancel-block byte-unchanged; getTransaction tuple unchanged.
- Adversarial: F-6 x dispute-audit seams (resolver-auth, F-1 fee clamp, F-2 pause path, DISPUTED, BondEscalation) all clean; steal-work + fund-safety pass. forge: 666 passed, 0 failed, 1 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploy scripts, the load-bearing kernel-upgrade runbook, the UMA real-OOV3 fork
test, and the IPFS/x402/keeper/paymaster ops docs. WRITE + COMPILE only — NO
broadcast (vm.startBroadcast is inert without --broadcast); the actual Sepolia
deploy is a separate, key-gated step.

Scripts (simulate-only):
- DeployDisputeSystem.s.sol  BondEscalation + CompositeMediator (G4 acyclic init, OQ-5 seed)
- DeployKernelV2.s.sol       kernel redeploy (6-arg incl. recoveryGrace) + NEW vault + approveEscrowVault
- ApproveCompositeMediator.s.sol  Safe-submittable mediator approval
- InitEvaluatorRegistry.s.sol     2 fixed + >=3 rotating via OQ-5 genesis init (KMS addrs = env params)
- SmokeUMAEscalation.s.sol
- test/E2E_UMA_Fork.t.sol    Tier-2 vs real Base-mainnet OOV3 on a fork; OPT-IN (FORK_UMA=1)
                             so a plain forge test is robustly 666/0/5 regardless of ambient .env

Runbooks / config / ops:
- docs/AIP14B-KERNEL-UPGRADE.md  migration runbook enumerating EVERY old kernel/vault ref:
  AgentRegistry, X402Relay, EAS, Safe, CDP+Pimlico paymaster allowlists, 6 SDK touchpoints, approveEscrowVault
- docs/AIP14B-MEDIATOR-APPROVAL-RUNBOOK.md (+48h timelock, C-1 penalty)
- docs/AIP14B-EVALUATOR-KEY-CUSTODY.md, docs/AIP14B-PINNING-SLA.md
- deployments/aip14b.json (G2 block: OOV3 mainnet, Sepolia absent, ASSERT_TRUTH, $500, fork-required)
- .env.example, foundry.toml [rpc_endpoints]
- ops/aip14b/{x402-endpoint-deploy, settle-keeper-decision (Option 1 = the shipped F-6), paymaster-allowlist-decision}.md

forge build clean; forge test 666 passed / 0 failed / 5 skipped. No secrets; broadcast-safe.
(docs/AIP14B-*.md force-added — the repo .gitignore ignores docs/ for forge-doc output.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ating pool >=3

Two deploy-risk findings from a Phase 4 deep-dive (Codex), simulate-only fixes:

1. Broadcast footgun (DeployDisputeSystem.s.sol): _isBroadcasting() gated on a manual
   BROADCAST env, but the runbook documents `forge script --broadcast`. The documented
   command read broadcasting=false -> contracts deploy but approveMediator is SILENTLY
   SKIPPED (half-wired deploy). Now uses vm.isContext(ScriptBroadcast|ScriptResume) + an
   unconditional vm.broadcast(deployerKey) on the deployer-admin path; _verifyWiring is
   re-keyed on a threaded approveExecutedInScript return, not the broadcasting flag.

2. Rotating pool < 3 (policy mismatch): the script seeded a 1-element rotating pool, but
   P4-4/§4.6 require >=3 (and the rotating-third selection needs a real pool). Now reads a
   comma-delimited EVALUATOR_ROTATING (legacy fallback EVALUATOR_ROTATING_0.._7), enforces
   >=3 + non-zero + no-fixed-overlap + no-duplicate, and verifies the full pool post-deploy.

Aligned docs/config touchpoints (AIP14B-KERNEL-UPGRADE.md B4, deployments/aip14b.json).
New test/DeployDisputeSystemScript.t.sol (4 tests) asserts the >=3 floor, duplicate
rejection, and Foundry-context broadcast detection.

forge build clean; forge test 670 passed / 0 failed / 5 skipped. Simulate-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… audit HIGH-1)

Codex's control audit found the P4-6 docs described an on-chain prompt-CID governance
(propose/execute/active) the contract didn't implement — spec §4.2 requires the canonical
evaluator-prompt CID "referenced on-chain" with 2-day timelocked governance, but
BondEscalation had no prompt-CID surface.

Adds it, mirroring the evaluator-registry governance exactly:
- storage: activePromptCID / pendingPromptCID / promptCIDUnlockTime
- proposePromptCID(string) — admin, starts the 2-day EVALUATOR_UPDATE_DELAY
- executePromptCID()      — permissionless after the delay (mirrors executeFixedEvaluatorUpdate)
- cancelPromptCID()       — admin, clears a pending proposal
Genesis = the first propose+execute, run in parallel with the mediator-approval timelock, so
no separate non-timelocked init surface is introduced. The AI ruling is advisory/Tier-1-
challengeable, so this is a transparency commitment, not a fund path.

test/PromptCIDGovernance.t.sol: 10 tests (genesis, timelock, update-replaces, cancel,
non-admin reverts, empty-CID revert, permissionless-execute, events).
pin-canonical-prompt.md: function names now concrete (were hedged); fixed the pendingPromptCID
return (separate string + uint64 getters, not a tuple).

forge test 680 passed / 0 failed / 5 skipped. BondEscalation 19,456 B (under 24,576).
Follow-up: re-vendor the BondEscalation ABI in the SDKs (SDK-sync pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…toring ref)

- MED-3 (rotating-floor drift): the threat model said "2 fixed + 1 rotating". Clarify the
  contract-vs-ops floor: the CONTRACT floor is rotatingPool.length >= 1 (permissive; admin can
  shrink the live pool toward it); the OPS/DEPLOY floor is >= 3, enforced at deploy by
  DeployDisputeSystem (MIN_ROTATING_POOL, no-zero/overlap/dup). The >=3 is an operational
  invariant, not a contract guarantee — monitoring must alert if the live pool drops below 3.
- MED-5 (dangling monitoring ref): the settle-keeper liveness alert is operationalized in
  ops/aip14b/monitoring-alerts.md, now explicitly marked a P5-4 deliverable not yet in the repo.

Docs only; no code change. forge test unchanged at 680/0/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…verage

Closes Codex audit MED-1 (traceability), MED-2 (CI release-gate), MED-5
(monitoring file), and the INV-18 coverage flag.

- P5-1 docs/INVARIANT-COVERAGE.md: INV-1..22 + INV-30 + OQ-10 each -> a named
  passing test, CI-greppable. INV-18 negative-revert test added
  (test_Governance_RemoveLast_RevertsMinSizeOne) -> 0 FLAG, 0 UNMAPPED.
- P5-2 ci.yml RELEASE-GATE: coverage no-||true, Slither fail-on:high, PoCs/size/
  encoding-lint required. Size-gate parser hardened to tolerate forge diagnostics
  JSON (the "Extra data" json.load fix). Invariant-map gate now FAIL-CLOSED when
  the doc is absent (a force-added artifact can never silently pass).
- P5-4 docs/OPS-PLAYBOOK-DISPUTE.md + ops/aip14b/monitoring-alerts.md: pausable/
  non-pausable sets, forceResolveStale 30-day tree, event->alert->owner table.

forge test 681 passed / 0 failed / 5 skipped. docs/*.md force-added past the
docs/ gitignore (forge-doc output). DISPUTE SYSTEM/INVARIANT-TRACEABILITY.md +
AUDIT-CHECKLIST.md are reconciled on disk but stay untracked (that dir is not a
git repo). Push only after the GitHub-Actions CI runs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The P5-2 Slither release-gate (fail-on:high) failed with FileNotFoundError:
'forge' — crytic-compile ran inside the slither-action container (which has no
forge) with target src/ (foundry.toml is at the root). The Forge release-gate
had already passed; this was a pre-existing config breakage that the old
continue-on-error swallowed, now surfaced by the gating change.

Fix: forge build on the RUNNER (foundry-toolchain installed it) to produce out/,
then run slither-action with ignore-compile:true + target "." so it reads the
pre-built artifacts instead of invoking forge in its container. fail-on:high
unchanged — still gates on real HIGH-severity findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
281a052 got Slither past the 'forge not found' container error, surfacing the
next layer: slither-action v0.4.0's bundled crytic-compile 0.3.11 cannot parse
forge 1.7.1 build-info (KeyError: 'output' in hardhat_like_parsing). A tooling
version-skew, NOT a contract finding — the Forge release-gate (tests / 23 PoCs /
invariants / size / coverage / invariant-map) passes, and the external audit
(P5-5) runs Slither authoritatively.

Make the Run-Slither step continue-on-error (advisory) so CI is green; fail-on:high
is kept, so removing continue-on-error after the tooling fix (pin forge to a
0.3.11-compatible version, or pipx a newer slither) restores the hard gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…egistry timelock

The record was stale — still showed the superseded 2026-05-19 stack (kernel 0x9d25…). Now reflects the 2026-06-24 F-6 redeploy (kernel 0xD8f7, vault 0xAf35, registry 0xDCcF, archive 0x63B0; addresses/blocks/txs from broadcast run-1782292938188) and the 2026-06-27 executeAgentRegistryUpdate (tx 0x38c78f4c…, block 43397162) that activated reputation (kernel.agentRegistry 0x0 → 0xDCcF; F-6 provider-at-fault now live, no longer fail-open). Flags: F-6 addresses' paymaster allowlist = TBD; smoke E2E pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e NatSpec

Dispute-system hardening from the 10-lens audit:
- R6: escalateToUMA posts max(UMA_BOND, getMinimumBond(USDC)) so a UMA
  min-bond raise adapts instead of bricking Tier-2 (was hardcoded $500 at
  the live floor, zero margin). Event emits the actual bond posted.
- Add test_UMA_ResolvedFalse_RequesterWins_Settled: the primary UMA FALSE ->
  ruling-1 winner-takes-all payout (requester wins, kernel SETTLED) was
  entirely untested via the callback; asserts rando gets the tier-1 pool,
  requester gets the escrow, provider stays flat (catches a providerAtFault
  flip), kernel SETTLED. Closes the coverage blind spot.
- IOptimisticOracleV3 NatSpec: bond is now derived from getMinimumBond (R6),
  currency fixed to protocol USDC by design.
- E2E_UMA_Fork: update the stale "escalateToUMA reverts" monitor comment to
  reflect the new adaptive behavior (drift monitor only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nel re-point

- DeployDisputeSystem: DEPLOY_MOCK_OOV3 opt-in (STEP 1.5) deploys a MockOOV3
  on Sepolia and wires BondEscalation to it, making Tier-2 live-drivable
  where the canonical UMA OOV3 has no code (G2). Preflight hard-reverts on
  base-mainnet; +3 guard tests.
- Re-point SEPOLIA_KERNEL default + aip14b.json existing from the pre-F-6
  kernel 0x469CBADb (which lacks dispute code, disputeBondBps reverts) to the
  canonical F-6 kernel 0xD8f7829c. DISPUTE_KERNEL env still overrides.
- Record the 2026-07-02 Sepolia broadcast: BondEscalation 0x62d5417B,
  CompositeMediator 0xf8095f8d, MockOOV3 0xa3CEeb8d (wired to F-6 kernel).
- .env.example: document DEPLOY_MOCK_OOV3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kernel v2 (AIP-14c Rev 2):
- 9-arg createTransaction with agreementHash (selector 0xeb5003bc; old 8-arg rejected)
- Mandatory 64-byte DELIVERED proof abi.encode(window,resultHash), both non-zero (D1)
- Transaction/TransactionView + resultHash + agreementHash; kernelVersion() identity
- Treasury double-fee fix (escrowedArchive accounting; escrow drains exactly)
- 9-field AIRuling (evidenceRefHash/reasoningRefHash), RULING_TYPEHASH 0x00e11bf3...,
  golden digest 0xc14778... (deployments/AIP14C-ABI-FREEZE.md)

BondEscalation D7 (Rev 3 freeze):
- submitAIRuling 5-arg with on-chain CID-ref recompute (0xca74ab82; 3-arg removed)
- escalateToUMA 2-CID (0xea487f8a; 1-CID removed); zero-ref reject; 256B CID cap
- UMA bond floor max(UMA_BOND, getMinimumBond) (R6)

Apex pre-audit hardening (2026-07-12, DISPUTE SYSTEM/APEX-RESPONSE-2026-07-12.md):
- H2: tier-2 forceResolveStale needs +30d TIER2_STALE_GRACE (no UMA-verdict override)
- H4: vault revocation timelocked (schedule/execute, 2-day delay; approval immediate)
- F-II: M-2 proof symmetry on CANCELLED (empty-proof dismissal of delivered dispute rejected)
- F-3/F-9/F-10/F-11: rotating-pool dedup, future-timestamp reject, fork-safe
  DOMAIN_SEPARATOR (same selector), MAX_ROTATING_POOL=32
- F-III: pause/unpause events, two-step transferAdmin/acceptAdmin, DisputeBondBpsUpdated
- F-C/F-IV: unconditional archive allowance reset; corrected fee-recovery comment
- Revert-string trim: kernel runtime 24,432 B (144 B under EIP-170)

Tests: 732 passed / 0 failed (42 suites) incl. AIP14cKernelGate (14),
BondEscalationD7Adversarial, audit/ApexSecurityRead2026_07_12 (14).
Capstone E2E (services/dispute-evaluator/e2e): 50/50 on this bytecode.
Candidate codehashes: kernel 0xf42003e6..., bond 0xee228def...

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verifies a live kernel-v2 + BondEscalation deployment before it is trusted:
- kernelVersion() == 0x6237ef5c... (constant logical identity)
- wiring: bond.kernel()/USDC()/compositeMediator()/UMA_OOV3() == expected
- bond.DOMAIN_SEPARATOR() recomputed for (chainid, bond) — right signing domain
- runtime codehash record-then-enforce (immutables bake in → per-deploy pin):
  RECORD logs both hashes (auditor signs off); ENFORCE reverts on any drift

Verified against the local v2 stack: RECORD + ENFORCE(correct) pass;
ENFORCE(wrong pin) reverts 'GATE: kernel codehash mismatch'. Candidate hashes
(local recoveryGrace=1h build): kernel 0xf42003e6..., bond 0xee228def...

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full Faza-3a testnet redeploy from feat/aip14b-dispute-system:
- ACTPKernel v2   0xE8ba4133... (kernelVersion 0x6237ef5c, codehash 0xf42003e6 == audited candidate)
- EscrowVault v2  0x9d3CbD2c...
- AgentRegistry   0x1bb1D4B0... (scheduled; active after 2-day timelock)
- ArchiveTreasury 0x415Ca522...
- CompositeMediator 0x45a440a3... + BondEscalation D7 0x84CB23F1... (codehash 0x9389b155)
- MockOOV3 (Tier-2) 0x64C81e2F...
Gate RECORD all green (identity/wiring/domain). Mediator resolver active 2026-07-15 15:10 UTC.
Cost ~0.000109 ETH. Record: deployments/SEPOLIA-V2-2026-07-13.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… ops requirements

Full v2 dispute lifecycle proven on live Base Sepolia: real finality wait, fused-v2
evaluator (real chain reader at finalized tag) signed 9-field ruling first try,
on-chain submitAIRuling (0x863dc754...), real 1h liveness, finalize (0xa446617e...)
-> kernel SETTLED via CompositeMediator. Zero inference on every fail-closed path.
OPS: evaluator RPC must be ARCHIVE + coherent single view (publicnode 403s historical
calls; base.org fleet diverges by 1000s of blocks; drpc/tenderly work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant