From 1eb9c61ac596707631a17f0af9fdadbdb2263e5b Mon Sep 17 00:00:00 2001 From: pdobacz <5735525+pdobacz@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:42:12 +0000 Subject: [PATCH 1/3] docs(skills): add the EIP branch merge skill Record how the single-EIP adoption branches consolidate onto one fork. Co-Authored-By: Claude --- .agents/skills/merge-eip-branches/SKILL.md | 155 +++++++++++++++++++++ .claude/skills/merge-eip-branches | 1 + AGENTS.md | 2 + 3 files changed, 158 insertions(+) create mode 100644 .agents/skills/merge-eip-branches/SKILL.md create mode 120000 .claude/skills/merge-eip-branches diff --git a/.agents/skills/merge-eip-branches/SKILL.md b/.agents/skills/merge-eip-branches/SKILL.md new file mode 100644 index 00000000000..d92a64d50b0 --- /dev/null +++ b/.agents/skills/merge-eip-branches/SKILL.md @@ -0,0 +1,155 @@ +--- +name: merge-eip-branches +description: Consolidate single-EIP adoption branches into one Monad fork that adopts all of them. +--- + +# Merge EIP Adoption Branches + +Consolidate several single-EIP adoption branches into one fork that adopts +all of them. Run this skill before starting such a merge. Each source +branch was built by `/adopt-upstream-eip`, alone, against its own upstream +EIP branch, so no source branch knows what the combined fork does. + +## Inputs + +- The source branches, and `` they all target. +- ``, the branch they merge into. Example: `forks/monad_nine`. +- `` and its upstream integration branch, which already has every + EIP composed. Example: `upstream/forks/amsterdam`. + +## Read First + +`/adopt-upstream-eip` defines the fork shape, the shared-file cost rules, +the step-12 cleanup list and the definition of done. This skill only covers +what the merge adds. + +## 1. Prepare + +```bash +git worktree add -b origin/ +git branch --unset-upstream +``` + +Merge into a fresh worktree, and never check out or rewrite a source +branch. Note which commits already landed on `` after the +source branches were cut: a branch carrying its own copy of one of them +conflicts, and ``'s version wins. + +```bash +git log --oneline $(git merge-base origin/ )..origin/ +``` + +## 2. Merge + +Merge the largest branch first, then the rest, with real merge commits. +Squashing or cherry-picking hides which branch each conflict came from. + +```bash +git merge # once per source branch +``` + +## 3. Resolve the Conflicts Git Reports + +Expect these, in rising order of thought needed: + +- **`.github/configs/feature.yaml`** — every branch appends its own entry + at the same point. Keep all entries for now; step 5 collapses them. +- **`forks.py`, the `` declaration** — each branch declares the + class with one mixin. The merged class lists every mixin ahead of the + parent fork, in ascending EIP order. Drop any dead `pass` left after + `follows()`. +- **CI files already on ``** — keep the base branch's version. +- **`src/ethereum/forks//`** — two EIPs changing the same file. + Resolve against the upstream integration branch, not against either + branch alone. + +What must merge silently: the `follows()` trait, the +`BaseForkMeta._is_subclass_of` walk, the `ValidAtTransitionTo` guard and the +exclusion conftests. They are byte-identical across branches by design. +Check afterwards that one copy of each survived. + +## 4. Find the Silent Merges + +This is the main risk. Two branches editing the same file at different +lines merge cleanly and produce behavior neither branch had. Git reports +nothing. + +Find the candidates from the upstream spec changes: + +```bash +for n in ; do + base=$(git merge-base upstream/eips//eip-$n) + echo "### EIP-$n" + git diff --stat $base..upstream/eips//eip-$n -- src/ethereum/forks/ +done +``` + +Any file listed under two EIPs needs reading in full on the merge branch. +Where one EIP removes what another adds, the merged file must match the +upstream integration branch, which already has both. + +Two shapes seen in practice: + +- One EIP adds a log for a state change a later EIP removes. Git keeps + both: a log for something that no longer happens. +- Two branches add the same conftest hook at different offsets, one after + the imports and one appended. Git keeps both hook definitions in one + module. + +Also read every file the merge touched outside `src/ethereum/forks/monad*` +and `tests/monad*`, whether or not git flagged it. + +## 5. Clean Up the Scaffolding + +Follow `/adopt-upstream-eip` step 12, driven by what the merged fork now +adopts: + +- Delete the exclusion conftest for each adopted EIP's suite. Those suites + must now be selected and pass. +- Collapse the per-branch feature entries into one. When the branch name + follows `eips//eip-+`, the feature name must carry the same + numbers in the same order: `check_release_matrix.py` matches the sequence + as a literal string. Verify it: + + ```bash + python3 .github/scripts/check_release_matrix.py "" + ``` + +- Revert expectation scaffolding written for an intermediate fork shape. A + test edit that only made a fork with EIP-A but without EIP-B green is + scaffolding once B joins. + +To test whether an edit was scaffolding, revert it and diff against the +base branch: + +```bash +git revert --no-commit && git diff origin/ -- +``` + +An empty diff means the edit existed only for the intermediate shape. +Reverting cleanly needs the source branch to have kept scaffolding in its +own commit; a commit mixing scaffolding with permanent change has to be +undone by hand. + +Keep gates on a capability (`fork.is_eip_enabled()`, `fork >= +MONAD_EIGHT`) that stay true on the merged fork. They are permanent. + +## 6. Validate + +Run `/adopt-upstream-eip`'s step-3 shape check, its step-5 `--collect-only` +selection check, then the full-suite fill for the fork. The full suite +matters: the merge changes which suites select the fork. + +Measure any exclusion that survived the cleanup, one suite at a time, by +stripping its hook and filling that suite. State which ones you did not get +to measure. + +## Definition of Done + +- Every source branch merged, with the merge commits kept. +- Each file two EIPs touched read in full and matched against the upstream + integration branch. +- One copy of each shared addition; no duplicated hook or trait. +- Adopted suites selected and passing; their exclusions deleted. +- One feature entry, its name matching the branch's EIP sequence. +- Full-suite fill green, with any unmeasured exclusion named. diff --git a/.claude/skills/merge-eip-branches b/.claude/skills/merge-eip-branches new file mode 120000 index 00000000000..4db213f200a --- /dev/null +++ b/.claude/skills/merge-eip-branches @@ -0,0 +1 @@ +../../.agents/skills/merge-eip-branches \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 063a9287dde..7b5c170566b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,7 @@ skills as `/name` commands through symlinked folders in `.claude/skills/`. - Implementing an EIP or modifying fork code in `src/` → run `/implement-eip` first - Adopting a single upstream EIP into a work-in-progress Monad fork → run `/adopt-upstream-eip` first - Merging upstream changes into `from-upstream` → run `/merge-from-upstream` first +- Consolidating several single-EIP adoption branches into one fork → run `/merge-eip-branches` first - Modifying GitHub Actions workflows → run `/edit-workflow` first - Assessing EIP complexity or scope → run `/assess-eip` - Working on EIP test coverage or checklists → run `/eip-checklist` first @@ -74,6 +75,7 @@ skills as `/name` commands through symlinked folders in `.claude/skills/`. - `/implement-eip` — fork structure, import rules, adding opcodes/precompiles/tx types - `/adopt-upstream-eip` — adopt one upstream EIP into a work-in-progress Monad fork and release its fixtures - `/merge-from-upstream` — merge upstream, propagate the changes to the Monad forks, verify the fixture set +- `/merge-eip-branches` — consolidate single-EIP adoption branches into one Monad fork - `/edit-workflow` — GitHub Actions conventions and version pinning - `/assess-eip` — structured EIP complexity assessment - `/eip-checklist` — EIP testing checklist system for tracking coverage From 8455c3975c372c0fbce99a210ec5146da48244d8 Mon Sep 17 00:00:00 2001 From: pdobacz <5735525+pdobacz@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:42:12 +0000 Subject: [PATCH 2/3] docs(skills): require a new fork to arrive as a replica of its parent A fork added in the same commit as its EIPs hides which lines are the adoption and which are the copy. Co-Authored-By: Claude --- .agents/skills/adopt-upstream-eip/SKILL.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.agents/skills/adopt-upstream-eip/SKILL.md b/.agents/skills/adopt-upstream-eip/SKILL.md index 034a5191c6d..feab958c20b 100644 --- a/.agents/skills/adopt-upstream-eip/SKILL.md +++ b/.agents/skills/adopt-upstream-eip/SKILL.md @@ -173,6 +173,12 @@ stay independent until a deliberate merge. ## 3. Adopt the EIP in the framework +Create the fork before adopting anything into it. Where `` +has no directory under `src/ethereum/forks/` yet, or no class in the +testing package, add them as exact replicas of `` in a +separate commit. The adoption then shows the EIPs as the only deviations +from the parent Monad fork, which is what the diff has to make visible. + In `packages/testing/src/execution_testing/forks/forks/forks.py`, declare `` **after** `` (so the name resolves) with the EIP mixins first: From a00f5bbb7a5b83969c8bd511ab874a6c84692460 Mon Sep 17 00:00:00 2001 From: pdobacz <5735525+pdobacz@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:59:35 +0000 Subject: [PATCH 3/3] docs(skills): close the gaps in the EIP branch merge skill Diff each EIP over its recorded range, keep every merge commit, decide scaffolding by filling, and read the feature check's printed list. Co-Authored-By: Claude --- .agents/skills/merge-eip-branches/SKILL.md | 31 +++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/.agents/skills/merge-eip-branches/SKILL.md b/.agents/skills/merge-eip-branches/SKILL.md index d92a64d50b0..6b1814d616c 100644 --- a/.agents/skills/merge-eip-branches/SKILL.md +++ b/.agents/skills/merge-eip-branches/SKILL.md @@ -45,9 +45,12 @@ Merge the largest branch first, then the rest, with real merge commits. Squashing or cherry-picking hides which branch each conflict came from. ```bash -git merge # once per source branch +git merge --no-ff # once per source branch ``` +`--no-ff` keeps the merge commit even when `` has not moved +since the branch was cut, where a plain merge would fast-forward. + ## 3. Resolve the Conflicts Git Reports Expect these, in rising order of thought needed: @@ -74,13 +77,16 @@ This is the main risk. Two branches editing the same file at different lines merge cleanly and produce behavior neither branch had. Git reports nothing. -Find the candidates from the upstream spec changes: +Find the candidates from the upstream spec changes, over the +`..` range each adoption recorded for its +EIP. Do not derive the range from the integration branch: upstream merges +some EIP branches with a merge commit, which makes the branch tip the +merge base and the diff empty. ```bash for n in ; do - base=$(git merge-base upstream/eips//eip-$n) echo "### EIP-$n" - git diff --stat $base..upstream/eips//eip-$n -- src/ethereum/forks/ + git diff --stat .. -- src/ethereum/forks/ done ``` @@ -109,10 +115,13 @@ adopts: - Collapse the per-branch feature entries into one. When the branch name follows `eips//eip-+`, the feature name must carry the same numbers in the same order: `check_release_matrix.py` matches the sequence - as a literal string. Verify it: + as a literal string. The script falls back to every feature when the + name does not match and still exits zero, so only its printed list + proves the name: ```bash - python3 .github/scripts/check_release_matrix.py "" + python3 .github/scripts/check_release_matrix.py "" \ + | grep 'features=\["monad_eip+"\]' ``` - Revert expectation scaffolding written for an intermediate fork shape. A @@ -126,10 +135,12 @@ base branch: git revert --no-commit && git diff origin/ -- ``` -An empty diff means the edit existed only for the intermediate shape. -Reverting cleanly needs the source branch to have kept scaffolding in its -own commit; a commit mixing scaffolding with permanent change has to be -undone by hand. +An empty diff only shows the revert is clean: nothing else depends on the +edit. Whether it was scaffolding is decided by filling the suites it +touched on the merged fork; keep the revert when they pass and keep the +edit when they fail. Reverting cleanly needs the source branch to have +kept scaffolding in its own commit; a commit mixing scaffolding with +permanent change has to be undone by hand. Keep gates on a capability (`fork.is_eip_enabled()`, `fork >= MONAD_EIGHT`) that stay true on the merged fork. They are permanent.