


Edit a Shared Smart-Contract Library Without Breaking Projects That Use It
Use when an AI coding agent works on a smart-contract library other projects inherit. It edits the file that owns the behavior, follows the linter, keeps existing functions, events, and storage layout unchanged, and rechecks risky code like cryptography and upgrades by hand.
To use this AI rule
## Build for projects that inherit the library, and agree on changes in an issue first
- This repository is a smart-contract library: reusable abstract contracts and libraries meant to be inherited and extended by downstream projects, not a deployed application. Most conventions exist because the code is a base for others.
- Because inheritors depend on it, design for extension: functions must be overridable where extension is expected, argument types must not over-constrain inheritors, and the public API must not force a particular call context onto child contracts.
- Read the repository's authoritative human-contributor guide first; it is the source of truth. An AI-assistant guide covers only what is specific to assisted work and the conventions not documented elsewhere. Where the two disagree, the human spec wins.
- Discuss any non-trivial change in an issue before opening a pull request. Get agreement on direction first rather than implementing unprompted, then open the PR against that agreed scope.
## Change the file that owns the behavior, and never hand-edit generated files
- A library repo has several distinct surfaces: library source (the public API), test-only mocks that mirror it, pristine interface definitions, auto-generated wrappers, formal-verification specs, documentation sources, and historical audit reports. Identify which surface owns a file before editing it, and change the surface that owns the behavior rather than an adjacent one. A single task may touch several surfaces at once, so do not assume a change is "just code" or "just docs" without checking.
- Never hand-edit an auto-generated or tool-managed file. A file procedurally generated from a template carries a header saying so: edit the template and regenerate instead, because CI enforces freshness with a generation-diff check and hand edits are overwritten. Auto-generated wrappers are typically gitignored and rebuilt on demand.
- Leave tool-maintained metadata to its tooling. A per-file "last updated version" header line is maintained automatically from version-control history by release tooling; do not add or edit it. Your responsibility is limited to what the tooling does not own, such as the license-identifier line.
## Leave naming and visibility rules to the linter, and skip them in reviews
- A configured linter already enforces the mechanical conventions on library source (mocks and tests are usually exempt): state variables default to private, with `constant` and `immutable` the only exceptions; an underscore prefix is matched to visibility, so private and internal state and functions take a leading underscore while public and external members do not; interface names are prefixed `I`; contracts and events use CapWords; parameters and modifiers use mixedCase; libraries expose only internal or private functions.
- If a lint rule already catches an issue, do not spend tokens explaining it in a code review; fix it and move on. Re-stating a lint-enforced rule in a review comment is noise, not signal.
## Follow the compiler-version and import rules the linter cannot catch
- Compiler pragma floor: do not hand-pick the minimum version. Run the project's pragma-minimization tool, which walks the dependency graph, compiles against each candidate compiler version, and writes back the lowest version that works per file (a `>=` prefix for interfaces, `^` for implementations and libraries). Bumping the floor casually can pull in opcodes that are unsupported on some target chains, so the tool's output is the safe answer.
- Imports must be 100% named, curly-brace, and relative within the source tree. No wildcard imports.
- Inheritance order must be globally consistent. If you change an `is A, B, C` list, recompile and run the inheritance-order check before pushing.
- Changelog entries: every change that alters contract behavior needs one. Skip it only for documentation-only edits, internal refactors with no user-visible effect, or pure repository plumbing.
## Keep existing functions and storage layout unchanged, and add few dependencies
- Released contracts are inherited downstream and are often transpiled into a separate upgradeable variant. Do not change the signatures of existing public or external functions, the shape of existing events or errors, or the storage layout of existing contracts. Make changes additive; deprecate rather than delete. A breaking change is a maintainer decision tied to a major release, never something to introduce during routine work.
- Keep the dependency surface minimal. Reuse the library's existing utility helpers before reaching outward. Do not add a third-party package or a new external import without maintainer sign-off; code that genuinely needs heavier third-party dependencies belongs in a separate community or extensions repository, not the core library.
## Recheck cryptography and storage-slot code by hand, where AI is often wrong
- In these areas AI suggestions are frequently plausible but wrong. Read the surrounding code and re-derive the claim before accepting any change:
- Cryptography: hashing, ECDSA, typed-data (EIP-712) domain separators, signature schemes, and pairing or BLS math.
- Storage-slot derivations, such as namespaced (ERC-7201) slots: recompute with the canonical derivation helper and compare against the declared constant. The formula in the inline comment is mandatory, not decorative.
- Storage layout for proxy- or transpiler-processed contracts: adding, removing, or reordering fields breaks already-deployed proxies. Read the storage-layout diff even though CI also runs it.
- `delegatecall`, inline assembly, and memory safety: verify that any memory-safe annotation is honest and that stack and scratch-space usage is bounded.
- `unchecked` blocks: re-derive the overflow-impossible argument for every code path. The inline justification comment is the contract you must uphold.
- Upgrade authorization: an authorize-upgrade hook is empty in the base by design, so every override must restrict who may call it.
- Proxy implementation constructors: confirm initializers are disabled (for example, a disable-initializers call) on UUPS or transparent-proxy implementation targets.
## Leave public API changes and security judgments to humans
- Adding or renaming public API, or deciding whether a feature belongs in the library at all: open an issue and get a maintainer decision first.
- Security assessments and audit opinions: defer to human auditors rather than asserting a contract is safe.
- New interface standards without explicit direction.
- Restructuring tests without a corresponding contract change.
- Never fabricate security rewards. Do not claim, imply, or invent a bug-bounty program, reward amount, or eligibility. Reference only an officially documented program if one genuinely exists.
