You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is the routing index for all instruction modules. It is automatically loaded as Tier 1 context.
Detailed rules live in the module files under .github/instructions/.
Scope, tasks, acceptance criteria for current iteration
Load context in tier order. Never duplicate higher-tier content into lower tiers — reference by filename.
Tier 2 and Tier 3 files may not exist in newly initialised repos or repos that were not bootstrapped by AI agents. When absent, rely solely on Tier 1 conventions and the codebase itself for context. Do not create these files unless explicitly requested.
Scope, tasks, acceptance criteria for current iteration
Master agents, executing subagents
Rules:
Never duplicate content from a higher tier into a lower tier — reference by filename.
Completed iteration files are historical context; only the active iteration file is the operative execution plan.
If a convention conflicts with a project-specific decision in PLAN.md, the project-specific decision takes precedence within that project.
Tier 2/3 files may not exist in newly initialised repos or repos not bootstrapped by AI agents. When absent, rely solely on Tier 1 conventions and the codebase itself for context. Do not create these files unless explicitly requested.
AGENT-02 · PR Decomposition [MUST]
Plan implementation as dependency-ordered, atomic pull requests where each PR compiles and tests independently.
Follow the build order in ARCH-01 (architecture.md) for sequencing.
Each PR should contain one logical unit of work (e.g., "add types + storage + interfaces", "add core module X", "add tests for module X").
Shared dependencies (types, storage, interfaces, test setup) must be merged before PRs that consume them.
Independent PRs (e.g., separate test files for separate modules) may be parallelized only after shared dependencies are merged.
AGENT-03 · Context Loading per Task Type [SHOULD]
Load only the instruction files relevant to the task. This minimizes context-window waste and improves agent focus.
When delegating to a subagent, include only the relevant files in the subagent's prompt context.
AGENT-04 · Commit Discipline [MUST]
One logical change per commit (see GIT-02 in git-workflow.md).
Align commit scope with touched modules — a commit that modifies MarketBetting.sol should not also modify MarketAdmin.sol unless the change is inherently cross-cutting.
Every commit must compile (forge build) and pass tests (forge test).
Run forge fmt and npx prettier --check . before each commit.
Consumers: Master agents, planning agents, architecture-review agents.
Load when: Planning new systems, designing modules, reviewing architectural decisions, or making any design choice that affects state, storage, accounting, or authorization.
These are durable design principles that govern all implementation decisions across iterations. They are not project-specific — they apply to any Solidity system built under this org's standards.
ARCH-01 · Build Order for New Systems [MUST]
When implementing a new protocol or module family, follow this dependency order:
This sequencing minimizes refactors and keeps each step independently reviewable.
ARCH-02 · Modular Composition [MUST]
Decompose behavior into focused modules (admin, lifecycle, execution, claims, view) and compose them in a thin orchestrator.
Keep shared cross-module logic in one base module/library to avoid drift and duplicate validation.
Interfaces are the canonical location for events and externally visible errors.
Storage libraries (namespaced via ERC-7201) are the single source of truth for shared state access.
ARCH-03 · State Machine Design [MUST]
Separate stored state (persisted lifecycle checkpoints) from effective state (runtime-derived from stored state + block.timestamp).
Compute time-dependent transitions at read/validation time — do not require maintenance transactions to advance state.
Gate each mutative function by the strict state required for that action.
For optional delay/grace mechanisms, support both paths explicitly:
Zero-delay path: single-step transition to final state.
Delayed path: pending state + explicit finalization step.
ARCH-04 · Accounting & Parameter Safety [MUST]
Use a single canonical internal unit for accounting; convert only at external transfer boundaries.
Snapshot mutable config (fees, rates, signer-dependent params) at irreversible state transitions — later admin updates must not retroactively affect settled outcomes.
Persist original aggregate accounting values when derivative balances may decrease over time (e.g., burn-on-claim). Do not derive final accounting from mutable balances.
Define deterministic fallback behavior for terminal edge cases (e.g., no valid winner → cancel/refund path).
For signed reports/actions, require EIP-712 typed structured data with domain separation, nonce, and expiry deadline.
Reject stale signatures and enforce monotonic/non-reusable nonces per context.
If corrections are allowed, confine them to an explicit pending window and reset pending timers/versions on update.
ARCH-06 · Test Strategy for Lifecycle Systems [MUST]
Use setup contracts with deterministic actors and helper functions for repeatable scenario construction.
Validate full lifecycle transitions (including timestamp-driven) without relying on implicit/manual steps.
Cover both revert and happy-path cases for every mutative function.
Add invariant/fuzz tests for conservation properties (e.g., payouts + fees never exceed pool; refunds match pool).
Include storage-slot correctness tests for all namespaced storage libraries.
ARCH-07 · Deployment & Environment [SHOULD]
Deploy scripts use run(string memory network, string memory environment) as the entry point. See STYLE-04 in solidity-style.md for the function signature convention. Further deployment conventions are pending documentation.
Optional. Module or area affected, e.g. market, staking, ci.
Description
Imperative mood ("add X", not "added X"), no trailing period, max ~72 characters.
Body
Optional. Separated by blank line. Explains why, not what.
Breaking
Append ! after type/scope: refactor(market)!: remove deprecated hook.
Allowed types:
Type
Purpose
feat
New feature or capability
fix
Bug fix
refactor
Code restructuring, no behavior change
test
Adding or updating tests
style
Formatting, linting, whitespace — no logic change
chore
Maintenance tasks (deps, configs, tooling)
ci
CI/CD pipeline changes
docs
Documentation only
perf
Performance improvement
build
Build system or external dependency changes
GIT-02 · Atomic Commits [MUST]
Each commit contains exactly one logical change matching its message.
Rule
Violation Example
No unrelated changes
A feat commit that also fixes formatting in another file
No bundling across types
Tests + refactor + feature in a single commit
Commit must compile and tests pass
A refactor commit that breaks compilation, fixed in the next one
Scope matches message
Message says "add whitelist" but commit also modifies staking logic
If a feature requires a preparatory refactor, that refactor is a separate commit (refactor: ...) before the feature (feat: ...).
GIT-03 · Branching & Pull Requests [MUST]
Rule
Details
One branch per work item
Each feature, fix, or refactor gets its own branch
Branch naming
type/short-kebab-description — e.g. feat/whitelist, fix/double-claim
Branch from main
Always branch off the latest main
Multiple focused commits per branch
A branch contains several atomic commits forming a reviewable unit
PR for review
Every branch merged via PR — no direct pushes to main
Each commit independently reviewable
Reviewers can step through commits one by one
GIT-04 · Formatting & Pre-commit Hooks [MUST]
Husky pre-commit hooks enforce formatting on every commit.
Pre-commit checks (.husky/pre-commit):
forge fmt --check
npx prettier --check .
Commit message lint (.husky/commit-msg):
npx --no -- commitlint --edit "$1"
Tool
Scope
Config
Fix command
forge fmt
Solidity (*.sol)
foundry.toml[fmt]
forge fmt
prettier
Non-Solidity (JS, JSON…)
.prettierrc
npx prettier --write .
commitlint
Commit messages
commitlint.config.js
Amend the message
Rules:
Never commit with --no-verify.
If forge fmt --check or prettier --check fails, fix and include formatting in the same commit — do not create a separate style: commit for code you are already touching.
style: commits are reserved for standalone formatting sweeps on unrelated files.
Storage and types follow the same service boundary rule: prefer MyContractStorage.sol and MyContractTypes.sol over cross-service shared files unless the shared type is intentionally cross-domain.
Cross-references are allowed when logically needed, but ownership remains service-local.
Standalone module errors may be declared in interfaces or contracts directly.
OZ-inherited errors follow OZ style: AccessManagerUnauthorizedAccount(address, uint64).
STYLE-08 · Error Handling [MUST]
Pattern
When to Use
require(condition, CustomError())
When the error is immediately obvious without application context (e.g., zero-address, zero-amount, simple access checks).
if (!condition) revert CustomError()
When the revert condition involves domain logic, multiple parameters, or benefits from the explicit if block for readability.
try/catch
Rarely used. No established best practices yet — avoid unless handling external-call failures where silent failure is unacceptable.
Layout (or XLayout when multiple exist, e.g. FundraiseLayout)
Accessor
function load() internal pure returns (Layout storage $)
STYLE-10 · NatSpec Comments [SHOULD]
Line length: All comments must respect the same line-length limit as code (foundry.toml[fmt] settings; Foundry default is 120). When a /// @dev one-liner exceeds the limit, convert to a /** @dev ... */ block.
Multi-line NatSpec: Always use collapsible block NatSpec for multi-line comments:
/** * @title MyContract * @notice Short description. */
Do not use stacked multi-line /// comments for contract-, library-, struct-, or function-level multi-line NatSpec.
Tag grouping order: When a NatSpec block contains multiple tag categories, keep them in this order:
@title
@notice and @dev together
@param entries together
@return entries together
@custom:* entries together
Leave exactly one blank line between each populated group.
/** * @title MyContract * * @notice Short description. * @dev Extra implementation context. * * @param account Account being updated. * @param amount Amount in asset units. * * @return success Whether the update succeeded. * * @custom:audit Uses pull-based settlement. */
Contract-level: Every contract/abstract/library (not interfaces) has @title + @notice. Add @custom:audit when deviating from upstream.
Structs: Put the NatSpec block immediately before the struct keyword.
Do not place routine field-by-field NatSpec comments inside the struct body.
When a field is not obvious without broader protocol context, document it in the struct-level NatSpec block.
Prefer an explicit field list in the block comment for non-obvious members, for example:
/** * @dev Persisted market data. * * @param state Stored lifecycle checkpoint used to derive the effective state. * @param pendingReportedAt Timestamp of the latest pending report inside the correction window. * @param feeAmount Fee amount snapshotted when the market resolves. */struct MarketData {
MarketState state;
uint40 pendingReportedAt;
uint256 feeAmount;
}
Use @param entries selectively for the fields that need extra context; obvious fields do not need redundant explanations.
Interfaces: No @title/@notice — the interface keyword is self-documenting.
Events / Errors: No NatSpec when self-documenting. /// @dev one-liner when extra context is needed.
Functions — Interfaces: No NatSpec when self-documenting. /// @dev for quick clarifications. /** @dev ... */ for complex behavior, reentrancy notes, or non-obvious side effects.
Functions — Implementations:
/// @inheritdoc IInterfaceName only when the interface function has NatSpec. Omit if there is nothing to inherit.
/// @dev one-liner for internal/private functions with non-obvious logic.
No NatSpec for trivial internals whose name is self-documenting.
STYLE-11 · Visibility & Mutability [MUST]
Principle
Rule
Visibility
external > public (if never called internally). private > internal (if only used in declaring contract).
Mutability
pure if no state access. view if read-only. Unmarked only when writing state.
virtual hooks
Use strictest mutability for the current implementation.
Modifiers: Keep modifiers as thin wrappers — call a private helper, then _;.
modifier restricted() {
__restricted();
_;
}
function __restricted() privateview {
if (msg.sender!= authority) revertUnauthorized(msg.sender);
}
STYLE-12 · Function & Declaration Ordering [SHOULD]
Section Headers
Major visibility groups use banner-style comments:
forge fmt uses Foundry default settings unless explicitly overridden in foundry.toml[fmt]. Only settings present in [fmt] are non-default. Common defaults (not repeated in config):
line_length = 120
tab_width = 4
bracket_spacing = false
Check the project's foundry.toml for active overrides. Always run forge fmt --check before committing.
Consumers: Test-writing agents, fuzz agents, review agents.
Load when: Writing, editing, or reviewing any test file. Also load solidity-style.md for naming rules.
TEST-01 · Directory Structure [SHOULD]
Tests mirror the src/ hierarchy. For single-domain projects a flat test/ layout is acceptable. For multi-domain projects, use subdirectories mirroring src/:
No givenX/whenY modifier pattern — use separate test contracts per feature instead.
TEST-07 · Mock Patterns [SHOULD]
Named ContractName_Mock
exposed_functionName() to surface internal functions
Direct state setters allowed for test convenience
contractMyContract_MockisMyContract {
function exposed_computeLeaf(...) externalviewreturns (bytes32) { ... }
function setToken(addresst_) external { ... }
}
TEST-08 · Event Testing [MUST]
Always use vm.expectEmit(true, true, true, true) to assert all event parameters (indexed and non-indexed). Only omit a parameter check (by setting the corresponding bool to false) when the value cannot be controlled or predicted in the test context (e.g., depends on third-party logic, block-level randomness, or is prohibitively complex to reproduce).
Reference events via their interface:
// ✅ Default — check all params
vm.expectEmit(true, true, true, true);
emit IMyContract.ActionCompleted(user, amount);
myContract.doAction(amount);
// ✅ Named parameter syntax is acceptable
vm.expectEmit(true, true, true, true);
emit IMyContract.ActionCompleted({user: alice, amount: 100});
myContract.doAction(100);
// ✅ Skipping a param — only when justified (e.g., third-party-generated value)
vm.expectEmit(true, true, false, true);
emit IMyContract.ActionCompleted(user, 0/* unknown amount from oracle */);
myContract.doAction(oracleData);
bound() over assume() [MUST]: bound() clamps the value into range; assume() discards the run. Use assume() only when the valid set cannot be expressed as a contiguous range.
function testFuzz_claim(uint256amount, uint256shares) public {
amount =bound(amount, 1, MAX_REALISTIC_AMOUNT);
shares =bound(shares, 1, amount);
// ...
}