Skip to content

refactor(forks): move fork-stable types to lean_spec.types (Stage 2 of #686)#695

Merged
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/move-fork-stable-types-stage2
May 1, 2026
Merged

refactor(forks): move fork-stable types to lean_spec.types (Stage 2 of #686)#695
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/move-fork-stable-types-stage2

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Stage 2 of the multi-fork architecture refactor (#686). Promotes data primitives whose shape is stable across the leanSpec fork chain out of forks/lstar/containers/ and into lean_spec.types, so the fork package contains only what genuinely changes per fork.

Moved to lean_spec.types:

  • Slot (with is_justifiable_after, justified_index_after, IMMEDIATE_JUSTIFICATION_WINDOW)
  • ValidatorIndex, SubnetId
  • Checkpoint

Stayed in the fork (intentionally):

  • Validator / Validators — XMSS-bound Bytes52 keys are signature-scheme specific.
  • ValidatorIndicesto_aggregation_bits couples it to the fork's AggregationBits; the decomposition (e.g. moving the conversion onto AggregationBits.from_validator_indices) is a clean follow-up.
  • Config — single-field today, but the most likely container to grow per fork (deposits, RANDAO, fee parameters). Both reviewers agreed promoting it to subspecs/chain/ now would just churn it back out.

Method placement: kept current methods on the moved types (Slot.is_justifiable_after, ValidatorIndex.is_proposer_for, etc.) rather than pre-emptively pushing them to the fork class. Per CLAUDE.md ("don't design for hypothetical future requirements"), the migration to fork-class methods can happen when divergence is real.

Notable changes

  • src/lean_spec/types/{slot,checkpoint,validator}.py — new files; re-exported from types/__init__.py.
  • src/lean_spec/forks/lstar/containers/{slot,checkpoint}.py — deleted.
  • src/lean_spec/forks/lstar/containers/validator.py — keeps only Validator, Validators, ValidatorIndices.
  • src/lean_spec/forks/lstar/containers/__init__.py — drops moved names (no re-export shim per CLAUDE.md).
  • ForkProtocol: validator_id: Uint64 | NoneValidatorIndex | None in SpecStoreType.from_anchor and ForkProtocol.create_store.
  • 130+ call sites across src/, tests/, and packages/ updated to import from lean_spec.types directly.

Reviewer input

Pre-implementation reviews from the consensus-researcher and py-architect agents confirmed the moves. They diverged on whether the methods (e.g. Slot.is_justifiable_after) should travel with the types or move to the fork class — the py-architect's "don't pre-emptively abstract" view prevailed and matches the CLAUDE.md guidance.

Test plan

  • uvx tox -e all-checks — passes (ruff, ruff-format, ty, codespell, mdformat).
  • uv run pytest --co — all 3290 tests collect cleanly.
  • CI pytest on this PR.

Refs #686

🤖 Generated with Claude Code

…leanEthereum#686)

Promote the data primitives whose shape is stable across the leanSpec fork
chain out of the lstar fork package and into lean_spec.types:

- Slot (with its 3SF-mini justifiability methods)
- ValidatorIndex, SubnetId
- Checkpoint

Validator and Validators stay in the fork because their XMSS-bound key shape
is signature-scheme specific. ValidatorIndices stays for now because
to_aggregation_bits couples it to the fork's AggregationBits; that
decomposition can land as a follow-up.

Config also stays in the fork: the single-field shape is the most likely
container to grow per fork (deposits, RANDAO, fee parameters), so promoting
it to subspecs/chain/ would just churn it back out later.

Tighten the ForkProtocol surface using the freshly promoted types:
SpecStoreType.from_anchor and ForkProtocol.create_store now take
ValidatorIndex | None instead of Uint64 | None.

Also drop the fork-side re-exports per CLAUDE.md (no compat shims) and
update all call sites across src/, tests/, and packages/.

Refs leanEthereum#686

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines 21 to +34
@@ -27,7 +31,7 @@ def justified_index_after(self, finalized_slot: Slot) -> int | None:
# Slot (finalized_slot + 1) maps to index 0.
return int(self - finalized_slot) - 1

def is_justifiable_after(self, finalized_slot: Slot) -> bool:
def is_justifiable_after(self, finalized_slot: "Slot") -> bool:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leolara Are we sure that these stuffs that are inside the Slot implementation should not be fork specific in case consensus rules evolve in the future?

@tcoratger tcoratger merged commit 62c5987 into leanEthereum:main May 1, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant