Skip to content

fix!(cdt): enforce Delaunay initialization invariants#123

Merged
acgetchell merged 3 commits intomainfrom
fix/62-delaunay-cdt-invariants
May 7, 2026
Merged

fix!(cdt): enforce Delaunay initialization invariants#123
acgetchell merged 3 commits intomainfrom
fix/62-delaunay-cdt-invariants

Conversation

@acgetchell
Copy link
Copy Markdown
Owner

  • Require constructor-grade Level 1-4 Delaunay validation before publishing labeled, strip, and toroidal CDT meshes.
  • Split evolved-state validation from initialization so completed simulations enforce structural PL-manifold, topology, foliation, causality, and cell-classification invariants without requiring Delaunay-ness.
  • Add validation cadence controls, checkpoint invariant checks, typed validation error categories, and regression coverage for the blocked periodic toroidal observables behavior.

BREAKING CHANGE: CDT validation and error APIs now use stricter constructor checks and typed error categories, including CdtValidationCheck, DelaunayValidationLevel, CheckpointResumeReason, CdtTopology, and MoveType fields in public error variants.

Closes #62

- Require constructor-grade Level 1-4 Delaunay validation before publishing labeled, strip, and toroidal CDT meshes.
- Split evolved-state validation from initialization so completed simulations enforce structural PL-manifold, topology, foliation, causality, and cell-classification invariants without requiring Delaunay-ness.
- Add validation cadence controls, checkpoint invariant checks, typed validation error categories, and regression coverage for the blocked periodic toroidal observables behavior.

BREAKING CHANGE: CDT validation and error APIs now use stricter constructor checks and typed error categories, including CdtValidationCheck, DelaunayValidationLevel, CheckpointResumeReason, CdtTopology, and MoveType fields in public error variants.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 1fb2da5b-e2d2-4c31-bd1c-a287661ba1dd

📥 Commits

Reviewing files that changed from the base of the PR and between 3f5a857 and f7c3605.

📒 Files selected for processing (3)
  • src/cdt/metropolis.rs
  • src/errors.rs
  • src/geometry/backends/delaunay.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/errors.rs
  • src/cdt/metropolis.rs
  • src/geometry/backends/delaunay.rs

Walkthrough

Centralizes and types CDT/Delaunay validation and errors, separates strict constructor (initial) vs evolved-state validation with configurable cadence, switches toroidal construction to a periodic-image Delaunay generator, removes toroidal post-move early rejection, and adds feature-gated slow integration and regression tests.

Changes

Core validation, backends, builders, wiring, and tests

Layer / File(s) Summary
Error & Validation Types
src/errors.rs, src/config.rs
Adds DelaunayValidationLevel, CdtValidationCheck, CheckpointResumeReason with Display; CdtError::DelaunayValidationFailed; several CdtError fields now use typed enums; CdtTopology implements Display.
Delaunay backend: serialization & cadence
src/geometry/backends/delaunay.rs
Persists/restores delaunay_check_policy via SerializableDelaunayCheckPolicy; adds validate_delaunay() (Level 1–4), validate_structural() (Level 1–3); mutation ops validate structural invariants and rollback on failure; cadence helpers and tests added.
Validation API & cadence wiring
src/cdt/triangulation/validation.rs, src/cdt/triangulation.rs
Introduces validate_initial_delaunay_cdt() (strict Level 4 + CDT checks); validate() delegates to evolved-state validation; adds set_delaunay_check_interval; checkpoint invariant validation routed through evolved validation; topology payloads use CdtTopology.
Builders & toroidal construction
src/cdt/triangulation/builders.rs, src/geometry/generators.rs
Refactors from_cdt_strip placement and uses validate_initial_delaunay_cdt(); toroidal constructor now uses build_periodic_toroidal_delaunay2; validate_toroidal_counts gains coordinate_range param; adds build_periodic_toroidal_delaunay2 and associated tests.
Ergodic moves & Metropolis integration
src/cdt/ergodic_moves.rs, src/cdt/metropolis.rs
Validation failures report CdtValidationCheck enums; removed toroidal_invariant_rejection post-move early-reject; checkpoint-resume paths use CheckpointResumeReason enum; sampling runs conditional validate_evolved_cdt_if_due.
Generators & re-exports
src/lib.rs, src/geometry/generators.rs
Adds periodic-toroidal generator export; expands crate prelude to re-export new error/validation enums/types.
Tests: unit, property, integration, regression
tests/*, src/cdt/*
Adds physics integration tests (tests/physics_integration.rs) with slow-tests gating, regression tests (tests/regressions.rs), Delaunay validation/cadence tests, many unit/property test updates to expect typed error enums and wording changes.
Tooling & docs
justfile, Cargo.toml, README.md, docs/*
Adds slow-tests Cargo feature and just test-slow recipe; documents prelude::errors, regression-test guidance, Delaunay initialization/validation contracts, and updates README wording to “Delaunay-built …” for CDT constructors.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant M as Metropolis
    participant T as CdtTriangulation
    participant D as DelaunayBackend
    participant C as Checkpoint/Serializer

    M->>T: request mutation candidate / step
    T->>D: apply mutation (insert/flip/subdivide)
    D->>D: validate_structural() (Level 1-3)
    alt structural failure
        D-->>T: ValidationFailed(Level 3) + detail
        D->>D: rollback snapshot
        T-->>M: mutation rejected
    else structural ok
        D-->>T: mutation accepted
        T->>T: validate_evolved_cdt_if_due()
        alt evolved validation due
            T->>D: validate_delaunay() (Level 1-4)
            D-->>T: OK / ValidationFailed(Level 4)
            T-->>M: continue or surface validation failure
        end
    end
    M->>C: serialize checkpoint
    C->>D: persist delaunay_check_policy
    C-->>M: checkpoint saved
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

🐇 I stitched the checks with tidy little ears,
Delaunay dances, cadence through the years,
Toroidal loops wrapped in periodic song,
Slow-tests wait patient, steady, and strong,
I hop, I validate—then bound along.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix!(cdt): enforce Delaunay initialization invariants' directly and clearly describes the main change: enforcing stricter Delaunay validation during CDT construction. It aligns with the core objective of the PR.
Description check ✅ Passed The description comprehensively explains the changes: constructor-grade Delaunay validation, split evolved-state validation, validation cadence controls, and breaking API changes. It clearly relates to the changeset and even references the closed issue.
Linked Issues check ✅ Passed The PR fully addresses issue #62's coding requirements: constructors enforce Level 1-4 Delaunay validation, evolved-state validation omits Delaunay-ness requirement, validation cadence is configurable, checkpoint restore validates structural contracts, and physics integration tests verify nontrivial simulation with nonzero moves and evolving measurements.
Out of Scope Changes check ✅ Passed All changes align with issue #62's scope. The PR introduces structured error types (CdtValidationCheck, DelaunayValidationLevel), splits initialization from evolved-state validation, adds test/documentation coverage, and intentionally excludes toroidal move tests (tracked in separate #122). No unrelated refactoring or feature scope creep detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/62-delaunay-cdt-invariants

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@acgetchell acgetchell enabled auto-merge May 7, 2026 02:49
@acgetchell acgetchell self-assigned this May 7, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 92.77778% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.49%. Comparing base (b854261) to head (f7c3605).

Files with missing lines Patch % Lines
src/cdt/metropolis.rs 78.00% 33 Missing ⚠️
src/geometry/backends/delaunay.rs 94.82% 9 Missing ⚠️
src/geometry/generators.rs 92.63% 7 Missing ⚠️
src/cdt/triangulation/builders.rs 96.62% 5 Missing ⚠️
src/cdt/triangulation/validation.rs 88.63% 5 Missing ⚠️
src/cdt/triangulation/foliation.rs 77.77% 4 Missing ⚠️
src/cdt/ergodic_moves.rs 97.14% 1 Missing ⚠️
src/config.rs 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #123      +/-   ##
==========================================
+ Coverage   91.96%   92.49%   +0.52%     
==========================================
  Files          19       19              
  Lines        9774    10315     +541     
==========================================
+ Hits         8989     9541     +552     
+ Misses        785      774      -11     
Flag Coverage Δ
unittests 92.49% <92.77%> (+0.52%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/proptest_foliation.rs (1)

71-93: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert toroidal cell classification here too.

This property now checks Delaunay/topology/foliation/causality, but it still never exercises validate_cell_classification(). A toroidal constructor regression that preserves the other invariants would still pass this test.

Based on PR objectives: constructors should enforce topology, foliation, causality, and strict cell classification before returning.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/proptest_foliation.rs` around lines 71 - 93, The test
toroidal_cdt_static_invariants currently never verifies cell classification;
after constructing the triangulation with CdtTriangulation::from_toroidal_cdt
and the existing Delaunay/topology/foliation/causality assertions, add an
assertion that tri.validate_cell_classification() returns Ok (e.g.
prop_assert!(tri.validate_cell_classification().is_ok())) so the toroidal
constructor is required to enforce strict cell classification before returning.
🧹 Nitpick comments (1)
tests/proptest_foliation.rs (1)

175-191: ⚡ Quick win

This “determinism” property only checks aggregate counts.

Two different meshes with the same counts and slice_sizes() will still pass here. Consider comparing a stable mesh fingerprint instead, such as serialized geometry or sorted vertex/face data, so the test actually detects nondeterministic strip construction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/proptest_foliation.rs` around lines 175 - 191, The test
cdt_strip_determinism currently only compares aggregate counts (vertex_count,
edge_count, face_count, slice_sizes) which can miss non-deterministic
differences; modify the test to compare a stable mesh fingerprint: call
CdtTriangulation::from_cdt_strip twice and serialize or produce a deterministic
canonical representation (e.g., sorted vertex list and sorted face/index lists
or a stable binary/JSON serialization) for t1 and t2, then assert equality of
those fingerprints instead of or in addition to the counts so the test detects
structural differences in the triangulation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/geometry/backends/delaunay.rs`:
- Around line 659-666: The validate_structural function currently calls
dt.tds().validate(), which only runs Levels 1–2 checks; change it to call
dt.as_triangulation().validate() so Level 3 topology/manifold checks are
included, and keep the existing map_err to convert the returned error into
DelaunayError::ValidationFailed with DelaunayValidationLevel::Three and the
error detail string; update the call site in validate_structural (method name
validate_structural, using self.dt) accordingly.

---

Outside diff comments:
In `@tests/proptest_foliation.rs`:
- Around line 71-93: The test toroidal_cdt_static_invariants currently never
verifies cell classification; after constructing the triangulation with
CdtTriangulation::from_toroidal_cdt and the existing
Delaunay/topology/foliation/causality assertions, add an assertion that
tri.validate_cell_classification() returns Ok (e.g.
prop_assert!(tri.validate_cell_classification().is_ok())) so the toroidal
constructor is required to enforce strict cell classification before returning.

---

Nitpick comments:
In `@tests/proptest_foliation.rs`:
- Around line 175-191: The test cdt_strip_determinism currently only compares
aggregate counts (vertex_count, edge_count, face_count, slice_sizes) which can
miss non-deterministic differences; modify the test to compare a stable mesh
fingerprint: call CdtTriangulation::from_cdt_strip twice and serialize or
produce a deterministic canonical representation (e.g., sorted vertex list and
sorted face/index lists or a stable binary/JSON serialization) for t1 and t2,
then assert equality of those fingerprints instead of or in addition to the
counts so the test detects structural differences in the triangulation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e29b2e90-debd-4060-a7b5-c2df4441d59e

📥 Commits

Reviewing files that changed from the base of the PR and between b854261 and 55cdbd4.

📒 Files selected for processing (27)
  • Cargo.toml
  • README.md
  • docs/code_organization.md
  • docs/dev/commands.md
  • docs/dev/rust.md
  • docs/dev/testing.md
  • docs/dev/tooling-alignment.md
  • docs/foliation.md
  • justfile
  • src/cdt/ergodic_moves.rs
  • src/cdt/metropolis.rs
  • src/cdt/observables.rs
  • src/cdt/results.rs
  • src/cdt/triangulation.rs
  • src/cdt/triangulation/builders.rs
  • src/cdt/triangulation/foliation.rs
  • src/cdt/triangulation/moves.rs
  • src/cdt/triangulation/validation.rs
  • src/config.rs
  • src/errors.rs
  • src/geometry/backends/delaunay.rs
  • src/geometry/generators.rs
  • src/lib.rs
  • tests/integration_tests.rs
  • tests/physics_integration.rs
  • tests/proptest_foliation.rs
  • tests/regressions.rs

Comment thread src/geometry/backends/delaunay.rs
- Validate Delaunay-backed mutations against the Level 1-3 structural contract before publishing edited geometry.
- Roll back insert, remove, flip, and subdivision edits that would leave invalid manifold structure or negative cell orientation.
- Treat toroidal checkpoint restore as invalid until upstream periodic-offset serialization preserves the data needed for structural validation.
- Strengthen foliation property coverage for toroidal cell classification and deterministic strip mesh structure.

BREAKING CHANGE: Delaunay-backed mutation and checkpoint restore paths now reject structurally invalid geometry that could previously be observed after successful edits or deserialization.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/geometry/backends/delaunay.rs (1)

2205-2247: ⚖️ Poor tradeoff

Consider an explicitly-broken fixture instead of relying on seed = 53.

The test exercises the rollback branch of validate_mutation_or_restore by assuming that flipping the first flippable edge in a seeded_delaunay2(8, (0.0, 10.0), 53) triangulation produces a Level 1–3 invalid geometry. That's a fragile coupling to upstream RNG/flip behavior — any unrelated change to the delaunay crate's seeding or candidate-edge ordering could make the flip succeed and silently turn this into a "rollback path never exercised" green test.

A more robust alternative is to drive the rollback path deterministically (e.g., feeding validate_mutation_or_restore a deliberately-corrupted snapshot via a test-only helper, or using with_cleared_neighbors_for_test to construct a known-invalid post-state). Not strictly required, but worth considering.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/geometry/backends/delaunay.rs` around lines 2205 - 2247, The test relies
on a fragile RNG seed (seeded_delaunay2(..., 53)) to produce a failing flip;
instead make the failure deterministic by constructing a known-broken post-flip
state and passing that into the rollback path. Modify
invalid_structural_flip_rolls_back_geometry to: create the base triangulation
via seeded_delaunay2 / DelaunayBackend::from_triangulation as before, pick a
flippable edge with edges()/can_flip_edge()/flip_edge, but after performing the
flip deliberately corrupt the topology using a test-only helper (e.g., call
with_cleared_neighbors_for_test or build a corrupted snapshot) so that
validate_mutation_or_restore (or flip_edge's internal call) will
deterministically hit the Level 1–3 validation failure and trigger rollback;
assert the same error shape and that vertex/edge/face counts and
validate_structural() are restored. Reference symbols:
invalid_structural_flip_rolls_back_geometry, seeded_delaunay2,
DelaunayBackend::from_triangulation, edges(), can_flip_edge, flip_edge,
validate_mutation_or_restore, with_cleared_neighbors_for_test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/errors.rs`:
- Around line 50-62: The Display impl for CdtValidationCheck is inconsistent:
change the ErgodicMoveCandidateGeometry arm in impl fmt::Display for
CdtValidationCheck to return the snake_case token (e.g.
"ergodic_move_candidate_geometry") instead of a space-separated phrase so it
matches the other variants used in CdtError::ValidationFailed; after changing
the formatter string, update the test assertion in
validation_check_display_covers_all_categories to expect the new
"ergodic_move_candidate_geometry" token.

---

Nitpick comments:
In `@src/geometry/backends/delaunay.rs`:
- Around line 2205-2247: The test relies on a fragile RNG seed
(seeded_delaunay2(..., 53)) to produce a failing flip; instead make the failure
deterministic by constructing a known-broken post-flip state and passing that
into the rollback path. Modify invalid_structural_flip_rolls_back_geometry to:
create the base triangulation via seeded_delaunay2 /
DelaunayBackend::from_triangulation as before, pick a flippable edge with
edges()/can_flip_edge()/flip_edge, but after performing the flip deliberately
corrupt the topology using a test-only helper (e.g., call
with_cleared_neighbors_for_test or build a corrupted snapshot) so that
validate_mutation_or_restore (or flip_edge's internal call) will
deterministically hit the Level 1–3 validation failure and trigger rollback;
assert the same error shape and that vertex/edge/face counts and
validate_structural() are restored. Reference symbols:
invalid_structural_flip_rolls_back_geometry, seeded_delaunay2,
DelaunayBackend::from_triangulation, edges(), can_flip_edge, flip_edge,
validate_mutation_or_restore, with_cleared_neighbors_for_test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 226c5511-8e80-41f8-b731-fa0aa16a7598

📥 Commits

Reviewing files that changed from the base of the PR and between 55cdbd4 and 3f5a857.

📒 Files selected for processing (5)
  • src/cdt/triangulation.rs
  • src/cdt/triangulation/moves.rs
  • src/errors.rs
  • src/geometry/backends/delaunay.rs
  • tests/proptest_foliation.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/cdt/triangulation.rs
  • tests/proptest_foliation.rs

Comment thread src/errors.rs
@acgetchell acgetchell added this to the v0.1.0 milestone May 7, 2026
- Emit `ergodic_move_candidate_geometry` consistently with the other
  `CdtValidationCheck` display identifiers.
- Keep validation regression coverage deterministic for structural Delaunay
  rollback and checkpointed Metropolis runs.
@acgetchell acgetchell disabled auto-merge May 7, 2026 05:24
@acgetchell acgetchell merged commit 94c6bde into main May 7, 2026
16 checks passed
@acgetchell acgetchell deleted the fix/62-delaunay-cdt-invariants branch May 7, 2026 05:27
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.

Validate evolving 1+1 CDT simulation pipeline

1 participant