Skip to content

Move the graph slice into chemvas.features.graph - #185

Merged
dhsohn merged 1 commit into
mainfrom
fix/graph-to-features
Aug 30, 2026
Merged

Move the graph slice into chemvas.features.graph#185
dhsohn merged 1 commit into
mainfrom
fix/graph-to-features

Conversation

@dhsohn

@dhsohn dhsohn commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Motivation

The three graph modules — algorithms, index operations, rotation policy — are
Qt-free policy that has been living in the legacy ui package. That is the
exact shape ADR 0001 says a feature package should own: their only outside
dependency is domain.document, and test_graph_algorithms_are_canvas_free
already enforces that no canvas reaches them. Unlike almost everything else in
ui, moving them costs no port, no bootstrap wiring and no runtime-services
change.

Changes

chemvas/features/graph/ with two real modules and no re-export layer,
following what the hover slice actually is — a single working module, not a
package of forwarding files.

algorithms.py holds the pure graph judgments plus the two rotation-policy
functions; __init__.py holds CanvasGraphState together with the index
operations, since cached_bond_in_cycle consumes the state and its
graph_version as a pair. The halves sit that way round for a measured reason:
the dependency runs index→algorithms, so giving __init__ the dependent half
keeps every import top-of-file and acyclic, where the opposite orientation
needs a bottom-of-file import from a partially-initialized package — which
E402 would reject anyway.

ui/canvas_graph_state.py shrinks to the twelve-line graph_state_for
accessor reading the runtime container field, the same leaf hover left behind;
it stays enumerated in EXPECTED_RUNTIME_STATE_ACCESSORS unchanged. The ten
modules that only use the accessor are untouched; the five function consumers
and two type consumers import from chemvas.features.graph — the package,
not its internals, which test_feature_callers_use_package_public_api
requires and now enforces for this feature too.

features.* is under the strict profile, and strict earned its keep here:
find_rings reused one variable first as an edge key (frozenset[int]) and
then as a ring signature (frozenset[frozenset[int]]) — a genuine type
collision the old Any-typed bonds had hidden. The bindings are separate names
now, and bond/atom/point inputs are typed as small structural protocols
(BondLike, AtomCoordsLike, PointLike) rather than Any. No Any was
introduced outside the accessor's conventional canvas.

The vacated ui module names join REMOVED_COMPATIBILITY_MODULES and
verify_dist's forbidden list; the new paths join its required list — both
following the pairing the chrome move and the hover slice established.

Verification

bash scripts/check.sh — exit 0: ruff clean, formatted, mypy clean, all 300
test files passing in their own processes. The 18 test files that name the
moved symbols pass together: 464 passed, 161 subtests passed.

Path-pinned checks that move with files are how a gate goes vacuous, so all
four were re-proven at the new locations: injecting canvas into both new
files fails test_graph_algorithms_are_canvas_free; a second reachability walk
fails test_seeded_graph_reachability_is_walked_in_one_place; a cache write in
canvas_graph_service fails test_bond_cycle_cache_has_one_writer; a second
importer fails test_cycle_membership_is_decided_in_one_module. Exit 1 each,
exit 0 restored, zero injection residue.

Packaging: a wheel and sdist from a clean git archive tree pass
verify_dist.py; a tampered wheel with chemvas/ui/graph_algorithms.py
re-inserted fails it, and one with algorithms.py removed fails it, each with
the exact message.

One history cost, stated: graph_rotation_policy.py merged into
algorithms.py, so git records it as a delete rather than a rename and
--follow loses its 99-line trail there. The other two files are recognized
renames.

The three graph modules were Qt-free policy living in the legacy ui
package — the exact shape ADR 0001 says a feature should own — with one
outside dependency (domain.document) and a canvas-free contract already
enforced by test. No port, no bootstrap wiring, no runtime-services
change was needed to move them.

The shape follows the hover precedent rather than a re-export package:
two real modules, no forwarding layer. algorithms.py holds the pure
graph judgments and the rotation policy; __init__.py holds
CanvasGraphState with the index operations, because cached_bond_in_cycle
needs the state and its graph_version as a pair. The halves sit that way
round because the dependency runs index-to-algorithms: giving __init__
the dependent half keeps every import top-of-file and acyclic, where the
opposite orientation needs a bottom-of-file import from a
partially-initialized package.

ui keeps only the twelve-line graph_state_for accessor reading the
runtime container field, the same leaf hover left behind. The ten
modules that only use the accessor are untouched.

features.* is strict, and strict earned its keep here: find_rings
reused one variable first as an edge key (frozenset[int]) and then as a
ring signature (frozenset[frozenset[int]]), a collision the old Any
bond typing had hidden. The bindings are now separate names, and the
bond, atom and point inputs are typed as small structural protocols
instead of Any.

The vacated ui modules join REMOVED_COMPATIBILITY_MODULES and
verify_dist's forbidden list, and the new paths its required list. The
four boundary checks that pin these modules by path were each re-proven
against an injected violation at the new locations.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-30T05:20:47.818439Z 94565a2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dhsohn
dhsohn merged commit 48ddacc into main Aug 30, 2026
8 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