refactor(spec): move observability seam into the spec layer#830
Open
alexanderlhicks wants to merge 1 commit into
Open
refactor(spec): move observability seam into the spec layer#830alexanderlhicks wants to merge 1 commit into
alexanderlhicks wants to merge 1 commit into
Conversation
The spec layer's last upward dependency on node was the observability import in fork_choice.py and state_transition.py. A prior refactor moved the Interval type and protocol constants into the spec layer but left this edge, so spec still reached up into node. Relocate node/observability/ to spec/observability/. The package is purely the vendor-neutral observer seam: a SpecObserver Protocol, a no-op default, the observe_* context-manager timers, and set_observer. Spec emits through it with a no-op default, so spec imports stay side-effect-free. The concrete Prometheus backend stays in node/metrics and the startup set_observer wiring stays in the CLI; only the seam moves. Pure relocation, no behavior change. The test moves to the mirrored spec path per the test-structure convention. After this, grep -rn "lean_spec.node" src/lean_spec/spec/ is empty: the spec layer no longer depends on node in either direction. Alternative considered: leave the seam in node and inject an observer into every spec entry point. Rejected as strictly worse churn that fights the existing module-level singleton design; the seam is itself a spec-level concern (the spec defines what it observes), so it belongs in spec with the no-op default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Just for context the two prior refactors were #819 and #817. I happened to basically have them done locally so this PR was tweaked at the last moment when pulling the latest changes from main.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes the
spec → nodedecoupling. A prior refactor relocated theIntervaltype and protocol constants into the spec layer but left oneupward dependency behind:
fork_choice.pyandstate_transition.pystill imported observability from
node. This moves that last edge.node/observability/→spec/observability/. The package is purely thevendor-neutral observer seam: a
SpecObserverProtocol, a_NullObserverno-op default, theobserve_*context-manager timers, andset_observer. The concrete Prometheus backend stays innode/metricsand the startup
set_observer(...)wiring stays in the CLI — only the seammoves. The test moves to the mirrored
tests/lean_spec/spec/observability/path per the test-structure convention.
After this:
The spec layer no longer depends on node in either direction.
Why move the seam (alternative considered)
The seam is already a dependency-injection design: spec emits through a
Protocol, the default is a no-op so spec imports stay side-effect-free, and
clients register a real observer at startup. That seam is a spec-level
concern — the spec defines what it observes — so it belongs in the spec
layer with the no-op default. The alternative, leaving it in
nodeandthreading an observer object through every spec entry point, was rejected:
it churns every state-transition signature and fights the existing
module-level singleton design.
Safety
Pure relocation, no behavior change — the observer classes and context
managers move verbatim; only import paths change.
Verification
Validated with
uv run --no-syncagainst the installed dependency set:grep -rn "lean_spec.node" src/lean_spec/spec/→ empty.ruff check(full) +ruff format --check→ clean.ty check→ clean.codespellon changed files → clean.pytest tests/lean_spec/spec/observability tests/lean_spec/spec/forks/lstar→ 243 passed.Note: full
just check/ fullpytestcould not be run locally becausemainbumpedlean-multisig-pytov0.0.6, whose maturin/Rust buildfails in my local environment (unrelated to this change). CI should run the
complete suite against a working build.
🤖 Generated with Claude Code