Skip to content

fix(delaunayize): make targeted PL repair failure-atomic#488

Merged
acgetchell merged 2 commits into
mainfrom
fix/304-delaunayize-targeted-repair
Jul 2, 2026
Merged

fix(delaunayize): make targeted PL repair failure-atomic#488
acgetchell merged 2 commits into
mainfrom
fix/304-delaunayize-targeted-repair

Conversation

@acgetchell

Copy link
Copy Markdown
Owner

Closes #304

  • Extend PL-manifold repair from facet over-sharing to bounded boundary-ridge, ridge-link, and vertex-link repair stages with typed diagnostics.
  • Run delaunayize through the Delaunay rollback transaction so failed topology repair, failed Delaunay repair, fallback snapshot failures, and rebuild failures restore the pre-call triangulation.
  • Expose PlManifoldRepairStage through the crate root and delaunayize prelude, and add benchmark fixtures for targeted topology repair.
  • Align local and CI tooling on Python 3.14, uv 0.11.26, and the reviewed Rust/Python tool pins.

Closes #304

Closes #304

- Extend PL-manifold repair from facet over-sharing to bounded boundary-ridge, ridge-link, and vertex-link repair stages with typed diagnostics.
- Run delaunayize through the Delaunay rollback transaction so failed topology repair, failed Delaunay repair, fallback snapshot failures, and rebuild failures restore the pre-call triangulation.
- Expose PlManifoldRepairStage through the crate root and delaunayize prelude, and add benchmark fixtures for targeted topology repair.
- Align local and CI tooling on Python 3.14, uv 0.11.26, and the reviewed Rust/Python tool pins.
@acgetchell acgetchell self-assigned this Jul 1, 2026
@acgetchell
acgetchell enabled auto-merge (squash) July 1, 2026 07:04
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 5de5cfec-3c61-4096-8525-238e92c2f138

📥 Commits

Reviewing files that changed from the base of the PR and between 9525033 and a6920bc.

📒 Files selected for processing (7)
  • .codacy.yml
  • benches/pl_manifold_repair.rs
  • docs/dev/tooling-alignment.md
  • notebooks/nonfaithful_embedding.ipynb
  • src/bench_fixtures.rs
  • src/core/algorithms/pl_manifold_repair.rs
  • src/delaunay/delaunayize.rs
✅ Files skipped from review due to trivial changes (1)
  • .codacy.yml
🚧 Files skipped from review as they are similar to previous changes (5)
  • benches/pl_manifold_repair.rs
  • docs/dev/tooling-alignment.md
  • src/core/algorithms/pl_manifold_repair.rs
  • src/bench_fixtures.rs
  • src/delaunay/delaunayize.rs

Walkthrough

This PR adds targeted PL-manifold repair stages, refactors delaunayize into transactional rollback/fallback handling, updates benchmark fixtures and exports, bumps CI/tooling pins for Python 3.14, and cleans up script CLI parsing, exception handling, and type hints.

Changes

Targeted PL-manifold repair feature

Layer / File(s) Summary
Repair stages and error model
src/core/algorithms/pl_manifold_repair.rs
Adds PlManifoldRepairStage, extends targeted repair errors, and updates repair-loop documentation and bounds.
Targeted repair orchestration
src/core/algorithms/pl_manifold_repair.rs
Implements the multi-stage repair loop, deterministic candidate selection, and targeted repair tests.
Transactional delaunayize rollback
src/delaunay/delaunayize.rs
Reworks fallback rebuild and rollback handling around DelaunayRollbackTransaction and structured snapshots.
Public exports and prelude coverage
src/lib.rs, tests/prelude_exports.rs
Re-exports PlManifoldRepairStage and updates export coverage tests.
Benchmark fixtures and docs
src/bench_fixtures.rs, benches/pl_manifold_repair.rs, benches/README.md
Adds targeted benchmark fixtures, new Criterion benchmarks, and updated benchmark documentation.
Changelog
CHANGELOG.md
Adds unreleased entries for the validation layer, failure-atomic mutations, and schema/tooling notes.

Estimated code review effort: 4 (Complex) | ~75 minutes

CI/tooling and Python 3.14 migration

Layer / File(s) Summary
Workflow and action pin updates
.github/workflows/*.yml
Bumps Rust toolchain, uv, cache, Python, audit, and other workflow pins.
Python and dependency baseline
.python-version, pyproject.toml, justfile, scripts/README.md, docs/dev/tooling-alignment.md
Raises the Python baseline to 3.14 and updates dependency/tool version pins and docs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Scripts CLI and exception handling cleanup

Layer / File(s) Summary
Argparse parser options
scripts/*.py, scripts/tests/*
Adds suggest_on_error=True and color=False to script entrypoints and adds typo-suggestion tests.
Shared exception families
scripts/benchmark_utils.py, scripts/hardware_utils.py, scripts/subprocess_utils.py, scripts/tests/*
Introduces named exception-family constants and replaces inline exception tuples in parsing and git checks.
Type hint and test import cleanup
scripts/benchmark_models.py, scripts/benchmark_utils.py, scripts/tests/conftest.py, scripts/tests/test_semgrep_fixture_config.py
Removes quoted forward references and moves typing-only imports under TYPE_CHECKING.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DelaunayizeByFlips
  participant DelaunayRollbackTransaction
  participant RepairPlManifoldTopology
  participant FinishDelaunayizeAfterDelaunayRepair
  DelaunayizeByFlips->>DelaunayRollbackTransaction: start transaction
  DelaunayizeByFlips->>RepairPlManifoldTopology: repair facet, ridge-link, and vertex-link violations
  RepairPlManifoldTopology-->>DelaunayizeByFlips: repair stats or error
  DelaunayizeByFlips->>FinishDelaunayizeAfterDelaunayRepair: finalize flip repair
  FinishDelaunayizeAfterDelaunayRepair->>DelaunayRollbackTransaction: commit or rollback
Loading

Possibly related PRs

Suggested labels: bug, enhancement, rust, geometry, topology

Poem

A rabbit hopped through topology,
found ridge-links in a hierarchy.
With snapshots neat and tools anew,
the burrow rolled back cleanly too. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the main change: making targeted PL repair failure-atomic in delaunayize.
Description check ✅ Passed The description aligns with the PR's main themes: targeted repair stages, rollback safety, and tooling alignment.
Linked Issues check ✅ Passed The PR adds bounded boundary-ridge, ridge-link, and vertex-link repair stages and wires them into delaunayize as requested in #304.
Out of Scope Changes check ✅ Passed The docs, tests, benchmarks, and tooling bumps appear to support the same repair/validation work rather than unrelated scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 100.00%.
✨ 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/304-delaunayize-targeted-repair

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Ruff (0.15.20)
notebooks/nonfaithful_embedding.ipynb

Unexpected end of JSON input


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

@codacy-production

codacy-production Bot commented Jul 1, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 211 complexity

Metric Results
Complexity 211

View in Codacy

🟢 Coverage 92.17% diff coverage · +0.02% coverage variation

Metric Results
Coverage variation +0.02% coverage variation (-1.00%)
Diff coverage 92.17% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (d6e9eea) 76010 69168 91.00%
Head commit (a6920bc) 76956 (+946) 70043 (+875) 91.02% (+0.02%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#488) 1086 1001 92.17%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot added bug Something isn't working enhancement New feature or request geometry Geometry-related issues rust Pull requests that update rust code topology labels Jul 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
src/delaunay/delaunayize.rs (1)

222-233: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Preserve attempted-repair stats on fallback recovery.

Both successful fallback paths return zeroed PlManifoldRepairStats / DelaunayRepairStats. That hides real work when topology repair exhausts its budget after removals or flip repair fails after checking facets/flipping cells, even though DelaunayizeOutcome exposes those fields as the repair-pass statistics. Please thread the partial stats/diagnostics into the recovered outcome instead of resetting them.

Also applies to: 753-757, 902-905

🤖 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/delaunay/delaunayize.rs` around lines 222 - 233, The fallback recovery
paths in DelaunayizeOutcome are overwriting attempted repair results with zeroed
PlManifoldRepairStats and DelaunayRepairStats, which hides the partial work
already done. Update the recovery logic in the DelaunayizeOutcome-producing flow
so the fallback success cases preserve and return the stats/diagnostics
accumulated by the failed topology repair or flip repair attempt instead of
resetting them. Focus on the code paths that build the recovered outcome and
thread the existing repair-pass stats through those branches.
🧹 Nitpick comments (3)
benches/pl_manifold_repair.rs (1)

87-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the benchmark label from fixture.stage.

bench_targeted_fixture takes a free-form stage_name even though TargetedTopologyRepairFixture already carries the canonical stage. One mismatched call site will silently mislabel the benchmark output.

Proposed refactor
-fn bench_targeted_fixture<const D: usize>(
-    group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>,
-    stage_name: &str,
-    fixture: &TargetedTopologyRepairFixture<D>,
-) {
+fn bench_targeted_fixture<const D: usize>(
+    group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>,
+    fixture: &TargetedTopologyRepairFixture<D>,
+) {
+    let stage_name = match fixture.stage {
+        delaunay::PlManifoldRepairStage::BoundaryRidgeMultiplicity => "boundary_ridge_multiplicity",
+        delaunay::PlManifoldRepairStage::RidgeLink => "ridge_link",
+        delaunay::PlManifoldRepairStage::VertexLink => "vertex_link",
+    };
+
     group.throughput(Throughput::Elements(
         u64::try_from(fixture.cluster_count).or_abort(),
     ));
 
     group.bench_with_input(
-        BenchmarkId::new(stage_name, format!("{}_clusters", fixture.cluster_count)),
+        BenchmarkId::new(format!("{stage_name}_{D}d"), format!("{}_clusters", fixture.cluster_count)),
         fixture,
         |b, fixture| {
             b.iter_batched(
🤖 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 `@benches/pl_manifold_repair.rs` around lines 87 - 111, The benchmark label in
bench_targeted_fixture is currently driven by a free-form stage_name instead of
the canonical stage stored on TargetedTopologyRepairFixture. Update
bench_targeted_fixture to derive the BenchmarkId name from fixture.stage, and
remove or stop relying on the external stage_name parameter so the label always
matches the fixture metadata. Make the change at the bench_targeted_fixture and
BenchmarkId::new call site, using the fixture field as the single source of
truth.
src/bench_fixtures.rs (1)

482-500: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the core stage-to-error mapping here.

manifold_error_matches_stage duplicates the ManifoldErrorPlManifoldRepairStage contract already defined in src/core/algorithms/pl_manifold_repair.rs. If that mapping changes upstream, these fixtures can start validating the wrong stage without any local compiler signal. Please funnel this through a shared helper owned by the repair/validation layer instead of keeping a second match table here.

As per coding guidelines, "Validation code must live at the lowest layer that owns the invariant."

🤖 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/bench_fixtures.rs` around lines 482 - 500, `manifold_error_matches_stage`
is duplicating the stage-to-error contract already owned by
`PlManifoldRepairStage` and `ManifoldError`, so move this check to a shared
helper in the repair/validation layer and call that helper from the bench
fixture instead of maintaining a second match table. Reuse the existing mapping
logic from `pl_manifold_repair` rather than pattern-matching again here, so
fixture validation stays aligned with the source of truth if the repair stages
change.

Source: Coding guidelines

src/core/algorithms/pl_manifold_repair.rs (1)

946-994: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the removal heuristic's provenance and conditioning.

simplex_quality_score now drives which simplex gets deleted, but its docs only name the aspect-ratio metric. They still don't explain how non-finite/invalid geometry is ranked (f64::MAX) or point readers to the algorithm reference, which makes the repair policy harder to audit. As per coding guidelines, "Algorithms must cite their source in REFERENCES.md and document conditioning behavior."

🤖 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/core/algorithms/pl_manifold_repair.rs` around lines 946 - 994, Document
the provenance and conditioning of simplex deletion in simplex_quality_score by
expanding its doc comment to cite the algorithm source in REFERENCES.md and
explain that invalid or non-finite geometry is ranked with f64::MAX. Keep the
explanation near the existing simplex_quality_score / repair-selection logic so
readers can see how the removal heuristic handles bad input and how it maps to
the referenced method.

Source: Coding guidelines

🤖 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/delaunay/delaunayize.rs`:
- Around line 718-725: The `snapshot_rebuild_state` handling in
`delaunayize_by_flips` is too lenient: when the post-topology fallback snapshot
is already invalid, the `Ok(delaunay_stats)` path still commits and returns
success. Update the branch around `DelaunayizeOutcome` so that a failed snapshot
rebuild after topology repair immediately aborts with a typed error, rolls back
the transaction, and does not reach `transaction.commit()`. Apply the same
fail-fast behavior anywhere the fallback snapshot is validated so the
inconsistency is surfaced right away instead of being deferred until later
repair logic.

---

Outside diff comments:
In `@src/delaunay/delaunayize.rs`:
- Around line 222-233: The fallback recovery paths in DelaunayizeOutcome are
overwriting attempted repair results with zeroed PlManifoldRepairStats and
DelaunayRepairStats, which hides the partial work already done. Update the
recovery logic in the DelaunayizeOutcome-producing flow so the fallback success
cases preserve and return the stats/diagnostics accumulated by the failed
topology repair or flip repair attempt instead of resetting them. Focus on the
code paths that build the recovered outcome and thread the existing repair-pass
stats through those branches.

---

Nitpick comments:
In `@benches/pl_manifold_repair.rs`:
- Around line 87-111: The benchmark label in bench_targeted_fixture is currently
driven by a free-form stage_name instead of the canonical stage stored on
TargetedTopologyRepairFixture. Update bench_targeted_fixture to derive the
BenchmarkId name from fixture.stage, and remove or stop relying on the external
stage_name parameter so the label always matches the fixture metadata. Make the
change at the bench_targeted_fixture and BenchmarkId::new call site, using the
fixture field as the single source of truth.

In `@src/bench_fixtures.rs`:
- Around line 482-500: `manifold_error_matches_stage` is duplicating the
stage-to-error contract already owned by `PlManifoldRepairStage` and
`ManifoldError`, so move this check to a shared helper in the repair/validation
layer and call that helper from the bench fixture instead of maintaining a
second match table. Reuse the existing mapping logic from `pl_manifold_repair`
rather than pattern-matching again here, so fixture validation stays aligned
with the source of truth if the repair stages change.

In `@src/core/algorithms/pl_manifold_repair.rs`:
- Around line 946-994: Document the provenance and conditioning of simplex
deletion in simplex_quality_score by expanding its doc comment to cite the
algorithm source in REFERENCES.md and explain that invalid or non-finite
geometry is ranked with f64::MAX. Keep the explanation near the existing
simplex_quality_score / repair-selection logic so readers can see how the
removal heuristic handles bad input and how it maps to the referenced method.
🪄 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: 27efc85e-c54d-49e2-9c11-3dc0fbf766d7

📥 Commits

Reviewing files that changed from the base of the PR and between d6e9eea and 9525033.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • .github/workflows/audit.yml
  • .github/workflows/benchmarks.yml
  • .github/workflows/ci.yml
  • .github/workflows/codacy.yml
  • .github/workflows/codecov.yml
  • .github/workflows/codeql.yml
  • .github/workflows/generate-baseline.yml
  • .github/workflows/profiling-benchmarks.yml
  • .github/workflows/release-benchmarks.yml
  • .github/workflows/rust-clippy.yml
  • .github/workflows/semgrep-sarif.yml
  • .python-version
  • CHANGELOG.md
  • benches/README.md
  • benches/pl_manifold_repair.rs
  • docs/dev/tooling-alignment.md
  • justfile
  • pyproject.toml
  • scripts/README.md
  • scripts/archive_changelog.py
  • scripts/benchmark_models.py
  • scripts/benchmark_utils.py
  • scripts/ci/filter_codacy_sarif.py
  • scripts/hardware_utils.py
  • scripts/notebook_check.py
  • scripts/postprocess_changelog.py
  • scripts/semgrep_fixture_config.py
  • scripts/subprocess_utils.py
  • scripts/tag_release.py
  • scripts/tests/conftest.py
  • scripts/tests/test_benchmark_utils.py
  • scripts/tests/test_hardware_utils.py
  • scripts/tests/test_notebook_check.py
  • scripts/tests/test_semgrep_fixture_config.py
  • scripts/tests/test_subprocess_utils.py
  • src/bench_fixtures.rs
  • src/core/algorithms/pl_manifold_repair.rs
  • src/delaunay/delaunayize.rs
  • src/lib.rs
  • tests/prelude_exports.rs

Comment thread src/delaunay/delaunayize.rs
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.16590% with 85 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.99%. Comparing base (d6e9eea) to head (a6920bc).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/core/algorithms/pl_manifold_repair.rs 90.71% 70 Missing ⚠️
src/delaunay/delaunayize.rs 95.46% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #488      +/-   ##
==========================================
+ Coverage   90.97%   90.99%   +0.01%     
==========================================
  Files          86       86              
  Lines       75789    76734     +945     
==========================================
+ Hits        68949    69823     +874     
- Misses       6840     6911      +71     
Flag Coverage Δ
unittests 90.99% <92.16%> (+0.01%) ⬆️

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

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

Roll back immediately when fallback simplex-data snapshots cannot be
validated, before topology or Delaunay repair recovery can commit. Preserve
repair diagnostics when fallback rebuild recovery succeeds.

- Deprecate the legacy Delaunay repair snapshot-error variant in favor of
  the phase-independent fallback snapshot error.
- Share the PL-manifold stage-to-error contract with benchmark fixtures and
  derive Criterion labels from validated fixture metadata.
- Add the non-faithful embedding notebook and document the Codacy uv.lock
  line-length exemption.
@acgetchell
acgetchell merged commit abd466f into main Jul 2, 2026
24 checks passed
@acgetchell
acgetchell deleted the fix/304-delaunayize-targeted-repair branch July 2, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request geometry Geometry-related issues rust Pull requests that update rust code topology

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: dedicated targeted topology repair stages (ridge, vertex-link)

1 participant