@@ -16,15 +16,17 @@ subspecifications that the Lean Ethereum protocol relies on.
1616## Development Workflow
1717
1818### Running Tests
19+
1920``` bash
2021uv sync # Install dependencies
2122uv run pytest # Run unit tests
22- uv run fill --fork=devnet --clean # Generate test vectors
23+ uv run fill --fork=devnet --clean -n auto # Generate test vectors
2324# Note: execution layer support is planned for future, infrastructure is ready
2425# for now, `--layer=consensus` is default and the only value used.
2526```
2627
2728### Code Quality
29+
2830``` bash
2931uv run ruff format src tests packages # Format code
3032uv run ruff check --fix src tests packages # Lint and fix
@@ -34,13 +36,15 @@ uvx tox # Everything (checks + tests + docs)
3436```
3537
3638### Common Tasks
39+
3740- ** Main specs** : ` src/lean_spec/ `
3841- ** Subspecs** : ` src/lean_spec/subspecs/{subspec}/ `
3942- ** Unit tests** : ` tests/lean_spec/ ` (mirrors source structure)
4043- ** Consensus spec tests** : ` tests/consensus/ ` (generates test vectors)
4144- ** Execution spec tests** : ` tests/execution/ ` (future - infrastructure ready)
4245
4346## Code Style
47+
4448- Line length: 100 characters, type hints everywhere
4549- Google docstring style (no docstrings for ` __init__ ` )
4650- Test files/functions must start with ` test_ `
@@ -54,15 +58,17 @@ uvx tox # Everything (checks + tests + docs)
5458 - * Note: ` tests/execution/ ` infrastructure is ready for future execution layer work*
5559
5660** Test Filling Framework:**
61+
5762- Layer-agnostic pytest plugin in ` packages/testing/src/framework/pytest_plugins/filler.py `
5863- Layer-specific packages: ` consensus_testing ` (active) and ` execution_testing ` (future)
5964- Write consensus spec tests using ` state_transition_test ` or ` fork_choice_test ` fixtures
6065- These fixtures are type aliases that create test vectors when called
61- - Run ` uv run fill --fork=Devnet --clean ` to generate consensus fixtures
66+ - Run ` uv run fill --fork=Devnet --clean -n auto ` to generate consensus fixtures
6267- Use ` --layer=execution ` flag when execution layer is implemented
6368- Output goes to ` fixtures/{layer}/{format}/{test_path}/... `
6469
6570** Example spec test:**
71+
6672``` python
6773def test_block (state_transition_test : StateTransitionTestFiller) -> None :
6874 state_transition_test(
@@ -73,6 +79,7 @@ def test_block(state_transition_test: StateTransitionTestFiller) -> None:
7379```
7480
7581** How it works:**
82+
76831 . Test function receives a fixture class (not instance) as parameter
77842 . Calling it creates a ` FixtureWrapper ` that runs ` make_fixture() `
78853 . ` make_fixture() ` executes the spec code (state transitions, fork choice steps)
@@ -81,12 +88,14 @@ def test_block(state_transition_test: StateTransitionTestFiller) -> None:
81886 . Writes fixtures at session end to ` fixtures/{layer}/{format}/{test_path}/... `
8289
8390** Layer-specific architecture:**
91+
8492- ` framework/ ` - Shared infrastructure (base classes, pytest plugin, CLI)
8593- ` consensus_testing/ ` - Consensus layer fixtures, forks, builders
8694- ` execution_testing/ ` - Execution layer fixtures, forks, builders
8795- Regular pytest runs (` uv run pytest ` ) ignore spec tests - they only run via ` fill ` command
8896
8997** Serialization requirements:**
98+
9099- All spec types (State, Block, Uint64, etc.) must be Pydantic models
91100- Custom types need ` @field_serializer ` or ` model_serializer ` for JSON output
92101- SSZ types typically serialize to hex strings (e.g., ` "0x1234..." ` )
@@ -97,6 +106,7 @@ def test_block(state_transition_test: StateTransitionTestFiller) -> None:
97106- Test the serialization: ` fixture.model_dump(mode="json") ` must produce valid JSON
98107
99108** Key fixture types:**
109+
100110- ` StateTransitionTest ` - Tests state transitions with blocks
101111- ` ForkChoiceTest ` - Tests fork choice with steps (tick/block/attestation)
102112- Selective validation via ` StateExpectation ` and ` StoreChecks ` (only validates fields you specify)
@@ -113,18 +123,21 @@ def test_block(state_transition_test: StateTransitionTestFiller) -> None:
113123When creating SSZ types, follow these established patterns:
114124
115125### Domain-Specific Types (Preferred)
126+
116127- Use meaningful names that describe the purpose: ` JustificationValidators ` , ` HistoricalBlockHashes ` , ` Attestations `
117128- Define domain-specific types in modular structure (see Architecture section below)
118129- Avoid generic names with numbers like ` Bitlist68719476736 ` or ` SignedAttestationList4096 `
119130
120131### SSZType vs SSZModel Design Decision
121132
122133** SSZType (IS-A pattern)** : Use for types that * are* data
134+
123135- Primitive scalars: ` Uint64 ` , ` Boolean ` , ` Bytes32 `
124136- These inherit directly from their underlying Python types
125137- Example: ` Uint64(42) ` * is* the integer 42 with SSZ serialization
126138
127139** SSZModel (HAS-A pattern)** : Use for types that * have* data
140+
128141- Collections: ` SSZList ` , ` SSZVector ` , bitfields
129142- Containers: ` State ` , ` Block ` , etc.
130143- These use Pydantic models with a ` data ` field for contents
@@ -150,6 +163,7 @@ src/lean_spec/subspecs/containers/
150163```
151164
152165** Key principles:**
166+
153167- ** Base types** (BaseBitlist, SSZList, etc.) stay in general scope (` src/lean_spec/types/ ` )
154168- ** Spec-specific types** go in their respective modules (` state/types.py ` , ` block/types.py ` )
155169- ** Public API** exposed through ` __init__.py ` files for backward compatibility
@@ -158,6 +172,7 @@ src/lean_spec/subspecs/containers/
158172### Examples
159173
160174** Good domain-specific types:**
175+
161176``` python
162177# In state/types.py
163178HISTORICAL_ROOTS_LIMIT = 262144
@@ -174,6 +189,7 @@ class Attestations(SSZList):
174189```
175190
176191** Avoid generic types:**
192+
177193``` python
178194# Don't do this:
179195class Bitlist68719476736 (BaseBitlist ): ...
@@ -183,5 +199,6 @@ class SignedAttestationList4096(SSZList): ...
183199### API Compatibility
184200
185201When refactoring, maintain backward compatibility:
202+
186203- Keep existing import paths working through ` __init__.py ` exports
187204- Preserve method signatures and behavior
0 commit comments