* Add AI assistant documentation and commands Adds structured documentation for AI coding assistants: - CLAUDE.md / AGENTS.md: Lightweight entry points with critical rules - .ai/: Shared knowledge base (CODE_REVIEW.md, DEVELOPMENT.md, ISSUES.md) - .claude/commands/: Claude Code skills for review, issue, release - .github/copilot-instructions.md: GitHub Copilot instructions Supports Claude Code, OpenAI Codex, and GitHub Copilot with modular, pointer-based structure for maintainability. Includes guidelines for AI assistants to prompt developers about updating these docs after receiving feedback, creating a continuous improvement loop. * Add parallel development tip with git worktrees * Address review feedback - Add missing details to DEVELOPMENT.md: fork-specific testing, database backends, cross-compilation targets, make test-release - Simplify AGENTS.md to pointer to CLAUDE.md (Codex can read files) * Address review feedback - Add priority signaling: Critical vs Important vs Good Practices - Restore actionable file references (canonical_head.rs, test_utils.rs, etc.) - Add Rayon CPU oversubscription context - Add tracing span guidelines - Simplify AGENTS.md to pointer * Address review feedback and remove Copilot instructions - Restore anti-patterns section (over-engineering, unnecessary complexity) - Restore design principles (simplicity first, high cohesion) - Add architecture guidance (dependency bloat, schema migrations, backwards compat) - Improve natural language guidance for AI comments - Add try_read lock pattern - Remove copilot-instructions.md (can't follow file refs, untestable)
7.5 KiB
Lighthouse Code Review Guidelines
Code review guidelines based on patterns from Lighthouse maintainers.
Core Principles
- Correctness over clever code
- Clarity through good documentation and naming
- Safety through proper error handling and panic avoidance
- Maintainability for long-term health
Critical: Consensus Crate (consensus/ excluding types/)
Extra scrutiny required - bugs here cause consensus failures.
Requirements
-
Safe Math Only
// NEVER let result = a + b; // ALWAYS let result = a.saturating_add(b); // or use safe_arith crate let result = a.safe_add(b)?; -
Zero Panics
- No
.unwrap(),.expect(), array indexing[i] - Return
ResultorOptioninstead
- No
-
Deterministic Behavior
- Identical results across all platforms
- No undefined behavior
Panic Avoidance (All Code)
// NEVER at runtime
let value = option.unwrap();
let item = array[1];
// ALWAYS
let value = option.ok_or(Error::Missing)?;
let item = array.get(1)?;
// Only acceptable during startup for CLI/config validation
let flag = matches.get_one::<String>("flag")
.expect("Required due to clap validation");
Code Clarity
Variable Naming
// BAD - ambiguous
let bb = ...;
let bl = ...;
// GOOD - clear
let beacon_block = ...;
let blob = ...;
Comments
- Explain the "why" not just the "what"
- All
TODOcomments must link to a GitHub issue - Remove dead/commented-out code
Error Handling
Don't Silently Swallow Errors
// BAD
self.store.get_info().unwrap_or(None)
// GOOD
self.store.get_info().unwrap_or_else(|e| {
error!(self.log, "Failed to read info"; "error" => ?e);
None
})
Check Return Values
Ask: "What happens if this returns Ok(Failed)?" Don't ignore results that might indicate failure.
Performance & Concurrency
Lock Safety
- Document lock ordering requirements
- Keep lock scopes narrow
- Seek detailed review for lock-related changes
- Use
try_readwhen falling back to an alternative is acceptable - Use blocking
readwhen alternative is more expensive (e.g., state reconstruction)
Async Patterns
// NEVER block in async context
async fn handler() {
expensive_computation(); // blocks runtime
}
// ALWAYS spawn blocking
async fn handler() {
tokio::task::spawn_blocking(|| expensive_computation()).await?;
}
Rayon
- Use scoped rayon pools from beacon processor
- Avoid global thread pool (causes CPU oversubscription)
Review Process
Focus on Actionable Issues
Limit to 3-5 key comments. Prioritize:
- Correctness issues - bugs, race conditions, panics
- Missing test coverage - especially edge cases
- Complex logic needing documentation
- API design concerns
Don't comment on:
- Minor style issues
- Things caught by CI (formatting, linting)
- Nice-to-haves that aren't important
Keep Comments Natural and Minimal
Tone: Natural and conversational, not robotic.
Good review comment:
Missing test coverage for the None blobs path. The existing test at
`store_tests.rs:2874` still provides blobs. Should add a test passing
None to verify backfill handles this correctly.
Good follow-up after author addresses comments:
LGTM, thanks!
or
Thanks for the updates, looks good!
Avoid:
- Checklists or structured formatting (✅ Item 1 fixed...)
- Repeating what was fixed (makes it obvious it's AI-generated)
- Headers, subsections, "Summary" sections
- Verbose multi-paragraph explanations
Use Natural Language
BAD (prescriptive):
"This violates coding standards which strictly prohibit runtime panics."
GOOD (conversational):
"Should we avoid `.expect()` here? This gets called in hot paths and
we typically try to avoid runtime panics outside of startup."
Verify Before Commenting
- If CI passes, trust it - types/imports must exist
- Check the full diff, not just visible parts
- Ask for verification rather than asserting things are missing
Common Review Patterns
Fork-Specific Changes
- Verify production fork code path unchanged
- Check SSZ compatibility (field order)
- Verify rollback/error paths handle edge cases
API Design
- Constructor signatures should be consistent
- Avoid
Optionparameters when value is always required
Concurrency
- Lock ordering documented?
- Potential deadlocks?
- Race conditions?
Error Handling
- Errors logged?
- Edge cases handled?
- Context provided with errors?
Deep Review Techniques
Verify Against Specifications
- Read the actual spec in
./consensus-specs/ - Compare formulas exactly
- Check constant values match spec definitions
Trace Data Flow End-to-End
For new config fields:
- Config file - Does YAML contain the field?
- Config struct - Is it parsed with serde attributes?
- apply_to_chain_spec - Is it actually applied?
- Runtime usage - Used correctly everywhere?
Check Error Handling Fallbacks
Examine every .unwrap_or(), .unwrap_or_else():
- If the fallback triggers, does code behave correctly?
- Does it silently degrade or fail loudly?
Look for Incomplete Migrations
When a PR changes a pattern across the codebase:
- Search for old pattern - all occurrences updated?
- Check test files - often lag behind implementation
Architecture & Design
Avoid Dependency Bloat
- Question whether imports add unnecessary dependencies
- Consider feature flags for optional functionality
- Large imports when only primitives are needed may warrant a
coreorprimitivesfeature
Schema Migrations
- Database schema changes require migrations
- Don't forget to add migration code when changing stored types
- Review pattern: "Needs a schema migration"
Backwards Compatibility
- Consider existing users when changing behavior
- Document breaking changes clearly
- Prefer additive changes when possible
Anti-Patterns to Avoid
Over-Engineering
- Don't add abstractions until needed
- Keep solutions simple and focused
- "Three similar lines of code is better than a premature abstraction"
Unnecessary Complexity
- Avoid feature flags for simple changes
- Don't add fallbacks for scenarios that can't happen
- Trust internal code and framework guarantees
Premature Optimization
- Optimize hot paths based on profiling, not assumptions
- Document performance considerations but don't over-optimize
Hiding Important Information
- Don't use generic variable names when specific ones are clearer
- Don't skip logging just to keep code shorter
- Don't omit error context
Design Principles
Simplicity First
Question every layer of abstraction:
- Is this
Arcneeded, or is the inner type alreadyClone? - Is this
Mutexneeded, or can ownership be restructured? - Is this wrapper type adding value or just indirection?
If you can't articulate why a layer of abstraction exists, it probably shouldn't.
High Cohesion
Group related state and behavior together. If two fields are always set together, used together, and invalid without each other, they belong in a struct.
Before Approval Checklist
- No panics: No
.unwrap(),.expect(), unchecked array indexing - Consensus safe: If touching consensus crate, all arithmetic is safe
- Errors logged: Not silently swallowed
- Clear naming: Variable names are unambiguous
- TODOs linked: All TODOs have GitHub issue links
- Tests present: Non-trivial changes have tests
- Lock safety: Lock ordering is safe and documented
- No blocking: Async code doesn't block runtime