| name | test-pr-devnet |
| description | Test ethlambda PR changes in multi-client devnet. Use when users want to (1) Test a branch/PR with other Lean clients, (2) Validate BlocksByRoot or P2P protocol changes, (3) Test sync recovery with pause/unpause, (4) Verify cross-client interoperability, (5) Run integration tests before merging. |
| disable-model-invocation | true |
Test PR in Devnet
Test ethlambda branch changes in a multi-client local devnet with zeam (Zig), ream (Rust), qlean (C++), and ethlambda.
Quick Start
.claude/skills/test-pr-devnet/scripts/test-branch.sh
.claude/skills/test-pr-devnet/scripts/test-branch.sh --with-sync-test
.claude/skills/test-pr-devnet/scripts/test-branch.sh my-feature-branch
.claude/skills/test-pr-devnet/scripts/check-status.sh
.claude/skills/test-pr-devnet/scripts/cleanup.sh
What It Does
- Builds branch-specific Docker image tagged as
ghcr.io/lambdaclass/ethlambda:<branch-name>
- Updates lean-quickstart config to use new image (backs up original)
- Starts 4-node devnet with fresh genesis (zeam, ream, qlean, ethlambda)
- Optionally tests sync recovery by pausing/unpausing nodes
- Analyzes results and provides summary
- Leaves devnet running for manual inspection
Prerequisites
| Requirement | Location | Check |
|---|
| lean-quickstart | /Users/mega/lean_consensus/lean-quickstart | ls $LEAN_QUICKSTART |
| Docker running | - | docker ps |
| Git repository | ethlambda root | git branch |
Test Scenarios
Basic Interoperability (~60-90s)
Goal: Verify ethlambda produces blocks and reaches consensus with other clients
Success criteria:
- ✅ No errors in ethlambda logs
- ✅ All 4 nodes at same head slot
- ✅ Finalization advancing (every 6-12 slots)
- ✅ Each validator produces blocks for their slots
Sync Recovery (~90-120s)
Goal: Test BlocksByRoot request/response when nodes fall behind
Usage: Add --with-sync-test flag
What happens:
- Devnet runs for 10s (~2-3 slots)
- Pauses
zeam_0 and qlean_0
- Network progresses 20s (~5 slots)
- Unpauses nodes → nodes sync
Success criteria:
- ✅ Inbound BlocksByRoot requests logged
- ✅ Successful responses sent
- ✅ Paused nodes sync to current head
Configuration Changes
The skill modifies lean-quickstart/client-cmds/ethlambda-cmd.sh to use your branch's Docker image.
Automatic backup: Creates ethlambda-cmd.sh.backup
Restore methods:
.claude/skills/test-pr-devnet/scripts/cleanup.sh
mv $LEAN_QUICKSTART/client-cmds/ethlambda-cmd.sh.backup \
$LEAN_QUICKSTART/client-cmds/ethlambda-cmd.sh
cd $LEAN_QUICKSTART && git checkout client-cmds/ethlambda-cmd.sh
Manual Workflow (Alternative to Script)
If you need fine-grained control:
1. Build Image
cd /Users/mega/lean_consensus/ethlambda
BRANCH=$(git rev-parse --abbrev-ref HEAD)
docker build \
--build-arg GIT_COMMIT=$(git rev-parse HEAD) \
--build-arg GIT_BRANCH=$BRANCH \
-t ghcr.io/lambdaclass/ethlambda:$BRANCH .
2. Update Configuration
Edit $LEAN_QUICKSTART/client-cmds/ethlambda-cmd.sh line 17:
node_docker="ghcr.io/lambdaclass/ethlambda:<your-branch> \
3. Start Devnet
cd $LEAN_QUICKSTART
NETWORK_DIR=local-devnet ./spin-node.sh --node all --generateGenesis --metrics
4. Test Sync (Optional)
docker pause zeam_0 qlean_0
sleep 20
docker unpause zeam_0 qlean_0
sleep 10
5. Check Results
.claude/skills/test-pr-devnet/scripts/check-status.sh
cd $LEAN_QUICKSTART
.claude/skills/devnet-log-review/scripts/analyze-logs.sh
Protocol Compatibility
| Client | Status | Gossipsub | BlocksByRoot |
|---|
| ream | ✅ Full | ✅ Full | ✅ Full |
| zeam | ✅ Full | ✅ Full | ⚠️ Limited |
| qlean | ✅ Full | ✅ Full | ⚠️ Limited |
| ethlambda | ✅ Full | ✅ Full | ✅ Full |
Notes:
- zeam/qlean BlocksByRoot errors are expected (not a blocker)
- ream ↔ ethlambda BlocksByRoot should work perfectly
- All clients use Gossipsub for block propagation
Verification Checklist
| Check | Command | Expected |
|---|
| All nodes running | docker ps --filter "name=_0" | 4 containers |
| Peers connected | docker logs ethlambda_0 | grep "Received status request" | wc -l | > 10 |
| Blocks produced | docker logs ethlambda_0 | grep "Published block" | wc -l | > 0 |
| No errors | docker logs ethlambda_0 | grep ERROR | wc -l | 0 |
Troubleshooting
Build Fails
docker ps
docker system prune -a
Nodes Won't Start
docker stop zeam_0 ream_0 qlean_0 ethlambda_0 2>/dev/null
docker rm zeam_0 ream_0 qlean_0 ethlambda_0 2>/dev/null
cd $LEAN_QUICKSTART
NETWORK_DIR=local-devnet ./spin-node.sh --node all --generateGenesis
Genesis Mismatch
cd $LEAN_QUICKSTART
NETWORK_DIR=local-devnet ./spin-node.sh --node all --cleanData --generateGenesis
Image Tag Not Updated
grep "node_docker=" $LEAN_QUICKSTART/client-cmds/ethlambda-cmd.sh
Port Already in Use
docker stop $(docker ps -q --filter "name=_0") 2>/dev/null || true
Debugging
P2P Request/Response Debugging
docker logs ethlambda_0 2>&1 | grep "Received BlocksByRoot request"
docker logs ethlambda_0 2>&1 | grep "Responding to BlocksByRoot"
docker logs ethlambda_0 2>&1 | grep "Sending BlocksByRoot request"
docker logs ethlambda_0 2>&1 | grep "Received BlocksByRoot response"
docker logs ethlambda_0 2>&1 | grep -E "Outbound request failed|protocol.*not.*support"
docker logs ethlambda_0 2>&1 | grep "Received BlocksByRoot request" | wc -l
Devnet Status Checks
docker ps --format "{{.Names}}: {{.Status}}" --filter "name=_0"
docker logs zeam_0 2>&1 | tail -100 | grep "CHAIN STATUS" | tail -1
docker logs ethlambda_0 2>&1 | grep "Fork choice head updated" | tail -5
docker logs ethlambda_0 2>&1 | grep "Received status request" | wc -l
Common Investigation Patterns
docker logs ethlambda_0 2>&1 | grep "We are the proposer"
for node in zeam_0 ream_0 ethlambda_0; do
echo "$node:"
docker logs "$node" 2>&1 | grep -i "finalized" | tail -1
done
docker logs ethlambda_0 2>&1 | grep -i "peer\|connection" | head -20
References