Skip to content

Commit bbfb352

Browse files
rayyan224claude
andauthored
docs: add release-cutting process to AGENTS.md (#220)
* docs: add release-cutting process and breaking-change guidance to AGENTS.md Document how versioning maps onto hardfork boundaries, what counts as a breaking vs. non-breaking change given this repo's slot-parity and stable-ABI invariants, the tag/branch/changelog steps to cut a release, and best practices for draft releases. Co-Authored-By: Claude <noreply@anthropic.com> * docs: inline backport procedure in AGENTS.md instead of referencing a skill The backport-loop skill is local tooling, not something every contributor has — spell out the tip-to-tip diff, commit-mapping, and cherry-pick/PR steps directly so the process stands on its own. Co-Authored-By: Claude <noreply@anthropic.com> * docs: make breaking-vs-non-breaking rules explicit for new errors on existing functions The old wording ("adding a new error that doesn't collide with an existing selector") read as if any new error were automatically non-breaking. Add a caller-observable-behavior test up front, split out the new-revert-path-on-an-existing-function case explicitly on both sides, and add a worked example to remove the ambiguity. Co-Authored-By: Claude <noreply@anthropic.com> * docs: distinguish additive storage growth from moving existing slots "Changing the storage slot layout for existing state" read as if any layout change were breaking, but appending new ERC-7201 namespaced state is additive and doesn't disturb existing slots (how Cobalt added new policy slots without touching Beryl's layout). Only moving, resizing, retyping, or reordering an already-shipped field's slot is actually breaking. Co-Authored-By: Claude <noreply@anthropic.com> * docs: decouple version bumps from hardfork boundaries The versioning rule tied MAJOR 1:1 to hardfork ordinals, which contradicts the breaking-vs-non-breaking test in the same section: Cobalt's actual changes (multiplier, seize, composite policies) are all non-breaking by that test, so forcing v2.0.0 for it was wrong. Switch to vMAJOR.MINOR.PATCH driven purely by the breaking test — MINOR for a hardfork's frozen (additive) interface, MAJOR only for an actual breaking change. Simplify branching to one ongoing branch per MAJOR line, since MINOR/PATCH releases no longer need their own branch or backport step; a second branch only appears once a breaking change actually ships. Co-Authored-By: Claude <noreply@anthropic.com> * docs: tighten breaking-vs-non-breaking wording Trim redundant explanatory clauses and merge overlapping revert-path bullets now that the section no longer needs to argue against the old hardfork-forces-MAJOR framing. Co-Authored-By: Claude <noreply@anthropic.com> * docs: drop the Backporting procedure from AGENTS.md Not applicable yet: releases/vN.x tracks main by fast-forward until a second MAJOR line actually exists, and none does. Document the detailed cherry-pick/PR mechanics when that first happens instead of carrying an untested procedure for a scenario that hasn't occurred. Co-Authored-By: Claude <noreply@anthropic.com> * docs: collapse Cutting releases into three subsections Fold Branching model, Process, and Draft releases into one Drafting a release list. The section is now just Major vs. minor, the Breaking change test, and Drafting a release — same substance, less scaffolding. Co-Authored-By: Claude <noreply@anthropic.com> * docs: drop the breaking/non-breaking bullet lists and example Keep the test itself as the operative rule; the enumerated cases and worked example were redundant with it. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent be6d045 commit bbfb352

1 file changed

Lines changed: 46 additions & 0 deletions

File tree

‎AGENTS.md‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,52 @@ Deeper reading: `LIVE_PRECOMPILE_TESTING.md` (cross-validation architecture), `d
8686
- CI on every PR: `forge build`, `forge test`, `forge fmt --check`,
8787
`python3 script/check-coverage.py`, coverage comment. Live-precompile tests run in a separate workflow.
8888

89+
## Cutting releases
90+
91+
### Major vs. minor
92+
93+
Versioning is `vMAJOR.MINOR.PATCH`, decided by the breaking change test below — not by hardfork
94+
boundaries. Hardforks here are additive by design, so most land as MINOR:
95+
96+
- **MAJOR**: an actual breaking change. Rare.
97+
- **MINOR**: a hardfork's frozen interface, once it's additive (the common case) — increments once
98+
per hardfork, matching `changelog/README.md`'s [Hardfork ordinals](changelog/README.md#hardfork-ordinals)
99+
shifted down by one (`01` Beryl → `1.0`, `02` Cobalt → `1.1`, ...). Also covers any other additive
100+
change. Freezing happens at tag time, which can be before on-chain activation.
101+
- **PATCH**: no interface change — tooling, harness, docs, or CI fixes (e.g. `v1.0.1`'s fork-profile
102+
pin).
103+
104+
If a hardfork's interface does contain a breaking change, that bumps MAJOR instead (resetting
105+
MINOR/PATCH to `0`).
106+
107+
### Breaking change test
108+
109+
This repo's invariant is slot-for-slot storage parity with the Rust precompiles plus a stable
110+
public ABI — a change is breaking if it violates either.
111+
112+
Fix an already-shipped selector and its inputs. If the outcome can differ from what it is today —
113+
succeeds where it reverted, reverts where it succeeded, reverts with a different error, or
114+
returns/emits something different — it's breaking, regardless of whether the ABI itself gained or
115+
lost anything. A new error or event isn't automatically non-breaking; check what it's reachable from.
116+
117+
### Drafting a release
118+
119+
There is one ongoing release branch per MAJOR line, `releases/vN.x`, fast-forwarded to `main` at
120+
each freeze point — no per-hardfork or per-minor branch. A new `releases/v(N+1).x` only gets cut the
121+
day a breaking change actually ships.
122+
123+
1. Land the frozen interface, its `changelog/<ordinal>_<Hardfork>_*.md` entries, and the
124+
`CHANGELOG.md` summary on `main`.
125+
2. Fast-forward `releases/vN.x` to that commit: `git push origin main:releases/vN.x`.
126+
3. Tag from that branch, draft first: `git tag vN.M.P && git push origin vN.M.P`, then
127+
`gh release create vN.M.P --draft --notes-file <notes>` — a draft anchored to a real, pushed tag
128+
(GitHub shows a synthetic `untagged-<hash>` URL for drafts; expected, resolves on publish).
129+
4. Write notes via `--notes-file` and proofread the rendered draft (`gh release view vN.M.P`) before
130+
leaving it — title/body follow `v1.0.0`/`v1.0.1`: compatibility statement, one bullet per
131+
changelog entry, link the aligned base/base tag if one exists.
132+
5. Keep at most one open draft per version. If the branch moves before publishing, delete
133+
(`gh release delete vN.M.P --yes`) and re-tag rather than leaving a stale draft around.
134+
89135
## Boundaries
90136

91137
- **Don't change the precompile addresses** in `src/StdPrecompiles.sol` or the feature IDs in

0 commit comments

Comments
 (0)