@@ -53,6 +53,9 @@ pnpm install
5353# Build all packages
5454pnpm build
5555
56+ # Build a specific package (faster iteration)
57+ pnpm --filter @lodestar/beacon-node build
58+
5659# Run linter (biome)
5760pnpm lint
5861
@@ -62,15 +65,17 @@ pnpm lint:fix
6265# Type check all packages
6366pnpm check-types
6467
68+ # Type check a specific package
69+ pnpm --filter @lodestar/beacon-node check-types
70+
6571# Run unit tests (fast, minimal preset)
6672pnpm test:unit
6773
68- # Run specific test file (faster - run from package directory)
69- cd packages/beacon-node
70- pnpm vitest run test/unit/path/to/test.test.ts
74+ # Run specific test file with project filter
75+ pnpm vitest run --project unit test/unit/path/to/test.test.ts
7176
7277# Run tests matching a pattern
73- pnpm vitest run -t " pattern"
78+ pnpm vitest run --project unit - t " pattern"
7479
7580# Run spec tests (requires downloading first)
7681pnpm download-spec-tests
@@ -81,6 +86,13 @@ pnpm test:spec
8186pnpm test:e2e
8287```
8388
89+ ** Tip:** For faster iteration, run tests from the specific package directory:
90+
91+ ``` bash
92+ cd packages/beacon-node
93+ pnpm vitest run test/unit/chain/validation/block.test.ts
94+ ```
95+
8496## Code style
8597
8698Lodestar uses [ Biome] ( https://biomejs.dev/ ) for linting and formatting.
@@ -94,6 +106,7 @@ Lodestar uses [Biome](https://biomejs.dev/) for linting and formatting.
94106- ** Types** : All functions must have explicit parameter and return types
95107- ** No ` any ` ** : Avoid TypeScript ` any ` type
96108- ** Private fields** : No underscore prefix (use ` private dirty ` , not ` private _dirty ` )
109+ - ** Named exports only** : No default exports
97110
98111### Import organization
99112
@@ -126,6 +139,94 @@ Metrics are critical for production monitoring:
126139- Do NOT suffix code variables with units (no ` Sec ` suffix)
127140- Time-based metrics must use seconds
128141
142+ ## Architecture patterns
143+
144+ ### Fork-aware code
145+
146+ Code that varies by fork uses fork guards and type narrowing:
147+
148+ ``` typescript
149+ import {isForkPostElectra , isForkPostFulu } from " @lodestar/params" ;
150+
151+ // Check fork before accessing fork-specific fields
152+ if (isForkPostElectra (fork )) {
153+ // electra and later forks
154+ }
155+ ```
156+
157+ The fork progression is: ` phase0 ` → ` altair ` → ` bellatrix ` → ` capella ` →
158+ ` deneb ` → ` electra ` → ` fulu ` → ` gloas ` .
159+
160+ ### Configuration
161+
162+ ` ChainForkConfig ` combines base chain config with computed fork information:
163+
164+ ``` typescript
165+ // Access config values
166+ config .SLOTS_PER_EPOCH ; // from params
167+ config .getForkName (slot ); // computed fork for a slot
168+ config .getForkTypes (fork ); // SSZ types for a fork
169+ ```
170+
171+ ` @lodestar/params ` holds constants (` SLOTS_PER_EPOCH ` , etc.).
172+ ` @lodestar/config ` holds runtime chain configuration.
173+
174+ ### State access
175+
176+ - Get current state via ` chain.getHeadState() ` — returns a tree-backed state
177+ - ** Never hold references to old states** — they consume memory and can go stale
178+ - For read-only access, use the state directly; for mutations, use ` state.clone() `
179+ - Beacon state is tree-backed (persistent data structure), making cloning cheap
180+
181+ ### SSZ types
182+
183+ Types use ` @chainsafe/ssz ` and come in two forms:
184+
185+ - ** Value types** : Plain JS objects. Easy to work with, higher memory usage.
186+ - ** View/ViewDU types** : Tree-backed. Memory-efficient, used for beacon state.
187+
188+ ``` typescript
189+ // Type definition
190+ const MyContainer = new ContainerType (
191+ {
192+ field1: UintNum64 ,
193+ field2: Root ,
194+ },
195+ {typeName: " MyContainer" }
196+ );
197+
198+ // Value usage
199+ const value = MyContainer .defaultValue ();
200+ value .field1 = 42 ;
201+
202+ // View usage (tree-backed)
203+ const view = MyContainer .toViewDU (value );
204+ view .field1 = 42 ;
205+ view .commit ();
206+ ```
207+
208+ ### Fork choice
209+
210+ The fork choice store uses proto-array for efficient head computation:
211+
212+ - ` getHead() ` returns a ** cached** ` ProtoBlock ` — may be stale after mutations
213+ - After modifying proto-array node state (e.g., execution status), call
214+ ` recomputeForkChoiceHead() ` to refresh the cache
215+ - This applies to any code that modifies proto-array outside normal block import
216+
217+ ### Logging
218+
219+ Use structured logging with metadata objects:
220+
221+ ``` typescript
222+ this .logger .debug (" Processing block" , {slot , root: toRootHex (root )});
223+ this .logger .warn (" Peer disconnected" , {peerId: peer .toString (), reason });
224+ ```
225+
226+ - Prefer structured fields over string concatenation
227+ - Use appropriate levels: ` error ` > ` warn ` > ` info ` > ` verbose ` > ` debug ` > ` trace `
228+ - Include relevant context (slot, root, peer) as structured fields
229+
129230## Testing guidelines
130231
131232### Test organization
@@ -157,11 +258,18 @@ for (const block of blocks) {
157258
158259### Running specific tests
159260
160- For faster iteration, run tests from the package directory :
261+ Use vitest project filters for targeted test runs :
161262
162263``` bash
264+ # Unit tests only (from repo root)
265+ pnpm vitest run --project unit test/unit/chain/validation/block.test.ts
266+
267+ # With pattern matching
268+ pnpm vitest run --project unit -t " should reject"
269+
270+ # From package directory (no project filter needed)
163271cd packages/beacon-node
164- pnpm vitest run test/unit/chain/validation/block.test.ts -t " should reject "
272+ pnpm vitest run test/unit/chain/validation/block.test.ts
165273```
166274
167275For spec tests with minimal preset (faster):
@@ -215,7 +323,8 @@ refactor(reqresp)!: support byte based handlers
215323- Keep PRs as drafts until ready for review
216324- Don't force push after review starts (use incremental commits)
217325- Close stale PRs rather than letting them sit
218- - Respond to review feedback promptly
326+ - Respond to review feedback promptly — reply to every comment, including bot reviewers
327+ - When updating based on feedback, respond in-thread to acknowledge
219328
220329## Common tasks
221330
@@ -241,6 +350,14 @@ refactor(reqresp)!: support byte based handlers
2413503 . The type will be automatically aggregated (no central ` sszTypes ` to modify)
2423514 . Run ` pnpm check-types ` to verify
243352
353+ ### Adding a new API endpoint
354+
355+ 1 . Define the route in ` packages/api/src/beacon/routes/<resource>.ts `
356+ 2 . Add request/response SSZ codecs alongside the route definition
357+ 3 . Implement the server handler in ` packages/beacon-node/src/api/impl/beacon/<resource>.ts `
358+ 4 . Add tests for the new endpoint
359+ 5 . Reference the [ Beacon APIs spec] ( https://github.com/ethereum/beacon-APIs ) for the endpoint contract
360+
244361## Style learnings from reviews
245362
246363### Prefer inline logic over helper functions
@@ -348,3 +465,21 @@ Edit source files in `packages/*/src/` instead.
348465
349466The ` specrefs/ ` directory contains pinned consensus spec versions.
350467When implementing spec changes, reference the exact spec version.
468+
469+ ## Common pitfalls
470+
471+ - ** Forgetting ` pnpm lint ` before pushing** : Biome enforces formatting. Always
472+ run it before committing. CI will catch it, but it wastes a round-trip.
473+ - ** Forgetting ` pnpm docs:lint ` after editing docs** : Markdown files are
474+ formatted by Prettier. Run ` pnpm docs:lint ` (or ` pnpm docs:lint:fix ` to
475+ auto-fix) before pushing changes to ` .md ` files.
476+ - ** Editing ` lib/ ` instead of ` src/ ` ** : Files in ` packages/*/lib/ ` are build
477+ outputs. Always edit in ` packages/*/src/ ` .
478+ - ** Stale fork choice head** : After modifying proto-array execution status,
479+ the cached head from ` getHead() ` is stale. Call ` recomputeForkChoiceHead() ` .
480+ - ** Holding state references** : Beacon state objects are large. Don't store
481+ references beyond their immediate use — let them be garbage collected.
482+ - ** Missing ` .js ` extension** : Relative imports must use ` .js ` even though
483+ source files are ` .ts ` . This is required for Node.js ESM resolution.
484+ - ** Force pushing after review** : Never force push once a reviewer has started.
485+ Use incremental commits — reviewers track changes between reviews.
0 commit comments