Skip to content

Chain suite: every --yes path on local anvil chains - #10

Merged
baairon merged 5 commits into
masterfrom
claude/wonderful-wright-obszfp
Oct 7, 2026
Merged

baairon merged 5 commits into
masterfrom
claude/wonderful-wright-obszfp

Conversation

@baairon

@baairon baairon commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Summary

npm run test:chain runs every command that sends a transaction against two local Anvil chains, so the --yes paths are proven on every pull request instead of only by live runs.

How it works (test/run-chain.mjs, test/chain/harness.ts):

  • Chains. One Anvil chain stands in for Ethereum Mainnet (ENS), the other for Base (the agent registry). The Base chain starts just past the registry's real start block, so owner lookups scan logs exactly as they do live.
  • Stand-in contracts (contracts/test/chain) sit at the real addresses: the ERC-8004 identity registry, the ENS registry, the public resolver, and the universal resolver viem uses. The real Vault is deployed by ethagent itself.
  • IPFS is served from the runner process through ETHAGENT_IPFS_API_URL, with raw CIDs so reads still verify the content hashes.
  • The wallet. A test wallet signs with Anvil's development keys in place of the browser tab. A request that names its account gets that one; otherwise the test picks the account, the way a person switches accounts.
  • No network. Every request to a host other than this machine is refused, and a test asserts it.

The lifecycle (test/chain/lifecycle.test.ts), one agent from start to finish. Each step is checked against chain state, not just exit codes:

  1. create, then a profile change;
  2. ENS link, then a records change;
  3. Advanced custody, with the operator key approved in the Vault;
  4. an operator save with no wallet, then a skip when nothing changed;
  5. check with nothing to fix;
  6. restore --owner while the Vault holds the token;
  7. restore --operator on a fresh machine, byte for byte;
  8. ENS delete;
  9. Simple custody, with operators revoked first;
  10. a transfer, after which the token moves and the receiver restores the agent.

It takes about 4 seconds.

Fixes the suite turned up:

  • transfer opened a second browser session of its own; it now runs in the command's single wallet tab, like every other command.
  • Saves with ETHAGENT_IPFS_API_URL demanded a Pinata JWT they never use, and exited 3. They now go through.
  • ETHAGENT_ENS_RPC_URL is new: it puts your own Mainnet RPC first for ENS reads and writes. There was no override before.

CI and docs:

  • CI gets a "Chain suite (anvil)" job, with Foundry from foundry-toolchain.
  • The README gets a Development section and the two environment variables.

Testing

  • npm run typecheck, npm test (776 pass), forge test (17 pass).
  • npm run test:chain: 14 of 14 pass locally, on Foundry 1.5.1.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HG2miyE22qTuug1Quxj7iw


Generated by Claude Code

claude and others added 5 commits October 7, 2026 01:36
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HG2miyE22qTuug1Quxj7iw
The signing step opened a second browser session of its own, the one command
that bypassed its wallet tab. It now takes the command's session when given
one, as create and the custody steps do; the manager still opens its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HG2miyE22qTuug1Quxj7iw
npm run test:chain starts a Mainnet and a Base anvil, places stand-in registry
and ENS contracts at the real addresses, serves IPFS from the runner, and runs
every --yes path in order with a test wallet in place of the browser: create,
profile, ENS link, records and delete, Advanced custody with the operator key,
an operator save, check, restore on a fresh machine and by owner lookup, Simple
custody, and a transfer the receiver restores. Every step is checked against
chain state, and nothing can reach a public host. CI runs it as its own job.

Along the way: ETHAGENT_ENS_RPC_URL for ENS reads and writes, and saves with
ETHAGENT_IPFS_API_URL no longer demand a Pinata JWT they never use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HG2miyE22qTuug1Quxj7iw
CI's Anvil read the bare genesis as pre-London and refused EIP-1559
transactions, so create failed and every later step had no agent. The genesis
now activates every fork through Cancun from block 0 with a base fee, and CI
pins Foundry to the version the suite was run with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HG2miyE22qTuug1Quxj7iw
- The heartbeat from #9 read a watcher that never touches its pid file
  as gone. That is every watcher started before #9, including 5.3.2's:
  status deleted its pid file, ensureDaemon started a second watcher
  beside it, and pause could not stop it.
- A pid that is alive but not beating is the daemon when its process
  is an ethagent watcher by its command line (/proc on Linux, ps on
  macOS, PowerShell on Windows). Any other live pid is still treated
  as recycled and is never signalled.
- A starting watcher stops such a watcher and takes the pid file only
  once that process is gone. A beating watcher is left in place.
- The heartbeat makes a watcher exit when sync is paused or another
  live process holds the pid file, and a watcher removes the pid file
  only while it is still its own.
- daemonStatus no longer deletes the pid file, so status is read-only
  again.
@baairon
baairon merged commit 2529766 into master Oct 7, 2026
4 checks passed
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.

2 participants