Skip to content

refactor(clock): relocate Interval to the spec layer and tidy SlotClock#819

Merged
tcoratger merged 2 commits into
leanEthereum:mainfrom
tcoratger:refactor/relocate-interval-to-spec-layer
Jun 1, 2026
Merged

refactor(clock): relocate Interval to the spec layer and tidy SlotClock#819
tcoratger merged 2 commits into
leanEthereum:mainfrom
tcoratger:refactor/relocate-interval-to-spec-layer

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Interval is the type of the store's time field, yet it lived in the node-layer clock module. That forced the consensus spec (spec.py, containers.py) to import Interval from the node layer — an inverted dependency that was papered over with # noqa: F821 # ty: ignore[unresolved-reference] suppressions on the Slot annotations in clock.py.

This PR relocates Interval to where it belongs and cleans up SlotClock, removing the suppressions entirely.

Changes

  • Relocate Interval to src/lean_spec/spec/forks/lstar/interval.py, next to the config and slot primitives it depends on. clock.py now imports Slot and Interval normally at module top — no noqa, no ty: ignore.
  • Delete the test-only Interval.from_unix_time. Its only callers were the fork-choice fixture tick path and the slot-clock conformance fixture; both now go through SlotClock.total_intervals, the node-layer owner of wall-clock→interval conversion. Generated test vectors are unchanged (verified bit-identical).
  • Derive current_slot from the millisecond base and drop the redundant seconds helper, removing a latent precision asymmetry between the slot and interval paths.
  • Mirror tests to source. Interval tests move to tests/lean_spec/spec/forks/lstar/test_interval.py; test_clock.py keeps only SlotClock. Adds a CLAUDE.md rule requiring the test tree to mirror the source tree.
  • Rewrite the tests as parametrized suites with cross-method consistency invariants (current_slot == total_intervals // INTERVALS_PER_SLOT, etc.), reaching 100% line and branch coverage for both modules.

Note: this branch is stacked on the unmerged refactor: move chain config to lstar fork spec commit, which the new clock.py import path depends on; it appears in the diff.

Testing

  • just check passes (ruff, format, ty, codespell, mdformat).
  • 66 unit tests pass; interval.py and clock.py at 100% coverage.
  • Slot-clock conformance fixtures regenerate cleanly.

🤖 Generated with Claude Code

tcoratger and others added 2 commits June 1, 2026 14:01
The chain and consensus configuration constants lived under
node/chain/config.py, but they are protocol parameters that client
teams must match, not node implementation details.

Several spec/ modules already imported them from node/, an inverted
dependency where the protocol-spec layer reached down into a specific
node implementation. SSZ container bounds (HISTORICAL_ROOTS_LIMIT,
MAX_ATTESTATIONS_DATA) and forkchoice/timing parameters belong with
the fork they define.

Move the file to spec/forks/lstar/config.py, scoping the constants to
the lstar fork in line with the per-fork registry pattern, and update
all import sites across src, tests, and packages/testing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Interval is the type of the store's time field, yet it lived in the
node-layer clock module. That forced the consensus spec to import from
the node layer, an inverted dependency papered over with F821 and
ty-ignore suppressions on the Slot annotations.

Move Interval into src/lean_spec/spec/forks/lstar/interval.py next to the
config and slot primitives it depends on. clock.py can now import Slot
and Interval normally at module top, and all suppression comments are
gone.

Further cleanups:

- Delete the test-only Interval.from_unix_time. Its two callers (the
  fork-choice fixture tick path and the slot-clock conformance fixture)
  now go through SlotClock.total_intervals, the node-layer owner of
  wall-clock to interval conversion. Generated vectors are unchanged.
- Derive current_slot from the millisecond base and drop the redundant
  seconds helper, removing a latent precision asymmetry between the slot
  and interval paths.
- Add a before-genesis guard equivalent so pre-genesis input clamps to
  zero instead of underflowing.
- Mirror the test layout to the source layout: Interval tests move to
  tests/lean_spec/spec/forks/lstar/test_interval.py. Add a CLAUDE.md rule
  requiring the test tree to mirror the source tree.
- Rewrite the Interval and SlotClock tests as parametrized suites with
  cross-method consistency invariants, reaching full line and branch
  coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 4ad1185 into leanEthereum:main Jun 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