Skip to content

refactor(cli): split __main__ into cli package with clean seams#763

Merged
tcoratger merged 9 commits into
leanEthereum:mainfrom
tcoratger:refactor/main-split
May 23, 2026
Merged

refactor(cli): split __main__ into cli package with clean seams#763
tcoratger merged 9 commits into
leanEthereum:mainfrom
tcoratger:refactor/main-split

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Replaces the 803-line `main.py` with a focused `cli/` package and introduces five reusable seams in surrounding subspecs.

Seams (subspec changes)

  • `Anchor` (`subspecs/node/anchor.py`) — value type unifying genesis-sync and checkpoint-sync boot under one return shape with two classmethod builders (`from_genesis`, async `from_checkpoint`).
  • `NodeConfig.anchor_store` — eliminates the post-hoc `node.store = checkpoint_store; node.sync_service.store = checkpoint_store` write the CLI used to perform on the checkpoint path.
  • `LiveNetworkEventSource.start_serving` — encodes the spec-required order for `set_status` → `set_current_slot_lookup` → dial → listen → gossipsub, and internalises the `_stop_event.clear()` poke.
  • `ValidatorRegistry.from_keys_directory` — owns the ream/zeam layout convention and raises on missing manifest (no more log-and-return-None).
  • `compute_subscription_subnets` (`subspecs/networking/gossipsub/subscription.py`) — pure helper for attestation subnet planning. Replaces the aggregator-no-validators → subnet 0 fallback.

CLI package layout

```
src/lean_spec/cli/
init.py # re-exports
args.py # CliArgs + parse_args (argparse boundary)
bootstrap.py # NodeBootstrap + CliValidationError (validation + resolution)
main.py # main() process entry, exit discipline
run.py # async run_node(boot) + _build_event_source(boot)
```

Dropped

  • `--genesis-time-now` — rewrote chain identity at runtime; spec-unsafe in production.
  • Aggregator + no validators → subnet 0 fallback — operationally incoherent, not derived from the spec.

Test layout

`tests/lean_spec/cli/` mirrors the source layout one-to-one. Non-CLI tests from the old kitchen-sink `test_cli.py` relocated to their proper subspec folders (`subspecs/node/test_anchor.py`, `subspecs/sync/test_checkpoint_sync.py`, `subspecs/networking/gossipsub/test_subscription.py`, `subspecs/validator/test_registry.py`).

Test plan

  • `uv run --group lint ruff check --no-fix` clean
  • `uv run --group lint ruff format --check` clean
  • `uv run --group lint ty check` clean
  • `uv run pytest tests/lean_spec/cli/` — 30 tests pass
  • `uv run pytest tests/lean_spec/cli/ tests/lean_spec/subspecs/node/test_anchor.py tests/lean_spec/subspecs/sync/ tests/lean_spec/subspecs/validator/test_registry.py tests/lean_spec/subspecs/networking/gossipsub/test_subscription.py` — 221 tests pass
  • `python -m lean_spec --help` renders correctly

🤖 Generated with Claude Code

tcoratger and others added 9 commits May 23, 2026 22:58
Replaces the 803-line __main__.py with a focused cli/ package and
introduces five reusable seams in surrounding subspecs:

- Anchor value type unifies genesis-sync and checkpoint-sync boot under
  one return shape with two classmethod builders.
- NodeConfig.anchor_store eliminates the post-hoc node.store assignment
  the CLI used to perform on the checkpoint path.
- LiveNetworkEventSource.start_serving encodes the spec-required order
  for status, current-slot lookup, dial, listen, and gossipsub start,
  and internalises the stop-event clear.
- ValidatorRegistry.from_keys_directory owns the ream/zeam layout
  convention and raises on missing manifest.
- compute_subscription_subnets is a pure helper for attestation subnet
  planning, replacing the aggregator-no-validators subnet-zero fallback.

The new cli/ package has five files: args.py (argparse boundary),
bootstrap.py (validation and resolution), main.py (process entry),
run.py (linear boot sequence), __init__.py (re-exports).

Drops two CLI-level conveniences that were spec-unsafe:

- --genesis-time-now, which rewrote chain identity at runtime.
- aggregator + no validators routed to subnet zero, operationally
  incoherent and not derived from the spec.

Tests mirror the source layout under tests/lean_spec/cli/, with
non-CLI tests relocated to the appropriate subspec test folders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The helper had one call site, a four-line body, and a dedicated test
file. Inlining it into the event-source builder removes a file, a
function, and a test module without losing any behavioural coverage,
since the surviving event-source tests already exercise the
empty-registry, validator-owned, and aggregator-extra paths through
the surrounding call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapse the multi-paragraph class docstring and bullet field
descriptions into single-line forms, split multi-clause sentences
across lines, and trim the three inline rationale comments.
No behavioural change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…genesis

Strip multi-clause sentences and dense prose blocks from three places:

- LiveNetworkEventSource.start_serving: collapse the five-step rationale
  into one-line bullets; rewrite the three inline comments as one
  sentence per line, drop ReqResp-specific protocol citations from the
  bullet text since they live in the inline comments.
- NodeConfig.anchor_store docstring: drop the redundant intro sentence;
  one sentence per line throughout.
- Node.from_genesis anchor-store branch: collapse the three-line
  rationale into two single-clause lines.

No behavioural change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The helper had one production call site and a five-test suite for
trivial pass-through behaviour. Inlining the block reconstruction
into the only caller removes a function, a docstring, and a test
class without losing meaningful coverage; the implicit success path
through the checkpoint anchor builder still exercises the zero
state-root recomputation branch.

The single test-setup usage in the node tests inlines the same
construction directly, since that test asserts identity on the
adopted anchor store and does not depend on a shared helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapse the multi-line classmethod signature, drop the "Convention:"
header in favour of a one-line lead-in, fold the validators.yaml
optional-file caveat into the Raises clause, and use one sentence
per line throughout.

No behavioural change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor the kitchen-sink module-level helpers into pytest fixtures
and tighten inline docs:

- Replace module-level keypair constants and ENR construction helpers
  with a session-scoped enr_keypair fixture plus a make_enr factory.
- Add named enr_with_udp / enr_without_udp fixtures for the two shapes
  used by current tests.
- Convert _write_aggregator_key_layout to a validator_keys_dir fixture.
- Merge bootstrap construction helpers into a single make_boot fixture
  that auto-prefixes the genesis flag.
- Drop module-level ENR_WITH_UDP / ENR_WITHOUT_UDP constants.
- Add byline one-line guides above each step of the two heaviest
  fixtures (make_enr and validator_keys_dir).
- Replace patch on Anchor.from_genesis with a real-call assertion on
  the resulting Anchor shape; keep the checkpoint patch with a
  rationale docstring.

All 16 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor the event-source builder tests to be readable top to bottom:

- Replace MagicMock event source with a 13-line _RecordingEventSource
  fake that captures the network name and topic subscriptions.
- Add a make_boot fixture that constructs NodeBootstrap with passive
  defaults overridable by keyword.
- Add a one_validator_registry fixture that holds a real precomputed
  XMSS keypair at index zero.
- Add a run_build fixture that patches the live transport factory and
  drives the builder against the recording fake.
- Collapse three single-test classes into one TestBuildEventSource
  class with three small tests.
- Drop the genesis_yaml fixture and CLI-driven boot construction;
  the bootstrap layer is tested in test_bootstrap.py.
- Add a third test for network-identity pinning, previously missing.

Three tests pass. No production code change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the multi-paragraph rationale on the module and class docstrings;
neither carried information that the surrounding code did not already
make obvious.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 51c7373 into leanEthereum:main May 23, 2026
6 of 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