Skip to content

refactor!(tds): canonicalize incidence storage#473

Merged
acgetchell merged 5 commits into
mainfrom
refactor/403-tds-incidence
Jun 21, 2026
Merged

refactor!(tds): canonicalize incidence storage#473
acgetchell merged 5 commits into
mainfrom
refactor/403-tds-incidence

Conversation

@acgetchell

Copy link
Copy Markdown
Owner
  • Split the TDS into focused storage, mutation, validation, incidence, equality, key, error, and snapshot modules.
  • Make vertex incidence canonical storage and expose AdjacencyIndex as a lifetime-bound borrowed view.
  • Remove compact summary error exports in favor of typed construction, insertion, repair, and invariant errors.
  • Add bench-gated PL-manifold repair fixtures and a Criterion benchmark for over-shared facets with orphan cleanup.
  • Align uv-managed workflow/tooling pins with uv 0.11.23 and guard raw benchmark fixture builders with Semgrep.

BREAKING CHANGE: AdjacencyIndex now carries a TDS lifetime and borrowed-view query signatures, compact summary error exports were removed from the root and focused preludes, and TDS internals are organized under src/core/tds/* instead of src/core/tds.rs.

Closes #403

- Split the TDS into focused storage, mutation, validation, incidence, equality, key, error, and snapshot modules.
- Make vertex incidence canonical storage and expose AdjacencyIndex as a lifetime-bound borrowed view.
- Remove compact summary error exports in favor of typed construction, insertion, repair, and invariant errors.
- Add bench-gated PL-manifold repair fixtures and a Criterion benchmark for over-shared facets with orphan cleanup.
- Align uv-managed workflow/tooling pins with uv 0.11.23 and guard raw benchmark fixture builders with Semgrep.

BREAKING CHANGE: AdjacencyIndex now carries a TDS lifetime and borrowed-view query signatures, compact summary error exports were removed from the root and focused preludes, and TDS internals are organized under src/core/tds/* instead of src/core/tds.rs.

Closes #403
@acgetchell acgetchell self-assigned this Jun 20, 2026
@acgetchell acgetchell enabled auto-merge (squash) June 20, 2026 05:51
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

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: d87454d2-2cd4-48e7-a15a-57b5ae0f4893

📥 Commits

Reviewing files that changed from the base of the PR and between e6e049e and 82b3147.

📒 Files selected for processing (1)
  • src/core/query.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/query.rs

Walkthrough

The PR adds maintained incidence storage and split topology views, refactors construction and insertion errors into typed boxed variants, updates repair and flip paths to traverse maintained incidence, and adds PL-manifold benchmark support with matching docs and tooling.

Changes

TDS modularization, typed errors, adjacency views, and benchmark plumbing

Layer / File(s) Summary
Benchmark wiring and policy
Cargo.toml, benches/README.md, benches/pl_manifold_repair.rs, docs/dev/*, semgrep.yaml, tests/semgrep/src/project_rules/rust_style.rs, src/bench_fixtures.rs, src/core/algorithms/pl_manifold_repair.rs, AGENTS.md, .github/workflows/*.yml, justfile, pyproject.toml
Benchmark recipes, fixture construction, Semgrep guidance, and workflow/tool pins are updated together for the PL-manifold repair benchmark path.
TDS storage, equality, and split topology views
src/core/tds/*, src/core/adjacency.rs, src/core/query.rs, src/delaunay/query.rs, src/lib.rs
TDS keys, errors, equality, incidence storage, snapshot hydration, and the split incidence/edge/neighbor topology views are introduced and wired through the public facades.
Incidence-driven repair, insertion, and flip control flow
src/core/insertion.rs, src/core/repair.rs, src/core/algorithms/flips.rs, src/topology/manifold.rs, tests/dedup_batch_construction.rs, tests/delaunay_edge_cases.rs, tests/regressions.rs, tests/large_scale_debug.rs
Repair, insertion, flip, and manifold code now use maintained incidence traversal and scoped repair frontiers; related regression and benchmark tests follow the new control flow.
Typed construction failures and retry model
src/core/algorithms/incremental_insertion.rs, src/core/construction.rs, src/core/tds/errors.rs, src/delaunay/construction.rs, src/delaunay/builder.rs, src/delaunay/insertion.rs, tests/triangulation_builder.rs, tests/prelude_exports.rs
Incremental insertion and Delaunay construction now route through structured typed errors, boxed sources, and retry-source tracking for periodic and repair failures.
Query facade and public API alignment
examples/triangulation_and_hull.rs, tests/public_topology_api.rs, tests/trait_bound_ergonomics.rs, src/delaunay/query.rs, src/lib.rs
The adjacency-index API surface is replaced with split topology builders and lifetime-bound views, with examples and public API tests updated to match.
Docs and regression alignment
docs/ORIENTATION_SPEC.md, docs/code_organization.md, docs/invariants.md, docs/numerical_robustness_guide.md, docs/production_review_remediation_checklist.md
Documentation cross-references are updated to the modular TDS layout and the new validation and topology-query module paths.
Other core query and equality updates
src/core/query.rs, src/core/tds/equality.rs, tests/example_workflows.rs, tests/public_topology_api.rs, tests/trait_bound_ergonomics.rs, tests/prelude_exports.rs
Core query internals complete the split-index migration and TDS equality adds deterministic geometric/incidence comparison with dedicated tests.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐇 I hopped through tds/ where the clean new paths glow,
Incidence remembers where all the simplices go.
validated_ fixtures rattle the repair-path tune,
While boxed typed errors bounce up like a moon.
The bench gets its carrots, the views stay lean and true,
And this rabbit says: “Split stacks make code leap, too!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor!(tds): canonicalize incidence storage' clearly summarizes the main change: refactoring the TDS module to make incidence storage canonical.
Description check ✅ Passed The description is comprehensive and relates directly to the changeset, covering TDS module splitting, incidence canonicalization, error refactoring, and additional features like benchmarks.
Linked Issues check ✅ Passed The PR meets objectives from #403: splits TDS internals into focused modules (storage, mutation, validation, incidence, equality, keys, errors, snapshot), maintains public API stability of crate::core::tds::Tds and TdsError, and keeps internal modules private.
Out of Scope Changes check ✅ Passed All changes are within scope: TDS refactoring, incidence canonicalization, error typing improvements, uv version updates, and benchmark/fixture additions directly support the PR objectives and linked issue #403.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 100.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 refactor/403-tds-incidence

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

@codacy-production

codacy-production Bot commented Jun 20, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 858 complexity

Metric Results
Complexity 858

View in Codacy

🟢 Coverage 89.03% diff coverage · -0.13% coverage variation

Metric Results
Coverage variation -0.13% coverage variation (-1.00%)
Diff coverage 89.03% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (1985e6f) 69013 63272 91.68%
Head commit (82b3147) 69935 (+922) 64023 (+751) 91.55% (-0.13%)

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 (#473) 8417 7494 89.03%

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 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: 3

Caution

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

⚠️ Outside diff range comments (3)
docs/code_organization.md (1)

450-468: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing tds/validation.rs from architecture inventory.

Line 468 references Level 2 validation as residing in tds/validation.rs, but the architecture overview (lines 450-457) does not include a tds/validation.rs entry. The listed modules are storage, errors, equality, keys, mutation, and snapshot, but validation is missing from the documented inventory.

Add tds/validation.rs to the architecture listing with a brief description matching its role as the Level 2 structural and adjacency consistency validator.

Proposed addition (after line 457)
  - `tds/snapshot.rs` - TDS persistence boundary: raw codec records parse into a validated UUID snapshot before hydration
    allocates fresh slotmap keys, preserving vertex and simplex payload data without serializing storage-local handles
+ - `tds/validation.rs` - TDS Level 2 structural validation and adjacency consistency checks

And verify that line 468 (the Level mapping comment) correctly reflects the modules documented above.

🤖 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 `@docs/code_organization.md` around lines 450 - 468, The documentation is
missing the tds/validation.rs module from the architecture inventory at lines
450-457, even though it is referenced later at line 468 in the validation.rs
section. Add a new entry for tds/validation.rs to the TDS modules list after the
tds/snapshot.rs entry with a brief description indicating it handles Level 2
structural validation and adjacency consistency checks. Then verify that the
Level mapping comment at line 468 correctly reflects all documented modules,
ensuring consistency between the architecture overview and the validation
orchestration description.
src/delaunay/construction.rs (1)

5128-5145: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Classify internal typed failures as non-retryable.

OrientationCanonicalizationInternal and InsertionNeighborWiring represent deterministic structural failures, but they currently fall through as retryable. With the default shuffled policy, these can be retried and finally reported as ShuffledRetryExhausted, obscuring the actionable typed source.

Proposed fix
                 DelaunayConstructionFailure::Tds {
                     reason: TdsConstructionFailure::DuplicateUuid { .. }
                         | TdsConstructionFailure::Validation { .. },
                 } | DelaunayConstructionFailure::InternalInconsistency { .. }
+                    | DelaunayConstructionFailure::InsertionNeighborWiring { .. }
+                    | DelaunayConstructionFailure::OrientationCanonicalizationInternal { .. }
                     | DelaunayConstructionFailure::UnsupportedPeriodicDimension { .. }
                     | DelaunayConstructionFailure::SpatialIndexConstruction { .. }
                     | DelaunayConstructionFailure::InsertionTopologyValidation { .. }
🤖 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/construction.rs` around lines 5128 - 5145, The
is_non_retryable_construction_error function is missing two deterministic
structural failure variants that should be classified as non-retryable. Add
OrientationCanonicalizationInternal and InsertionNeighborWiring to the pattern
match within the matches! macro by including two additional match arms
(OrientationCanonicalizationInternal { .. } and InsertionNeighborWiring { .. })
as DelaunayConstructionFailure variants in the same list as the other
non-retryable failure types, ensuring these failures are not retried and their
typed source is preserved.
src/core/algorithms/flips.rs (1)

430-432: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Surface stale incidence instead of skipping it.

simplex_keys_containing_vertex now reads the canonical incidence index, so an unresolved simplex key is an invariant violation. The current continue can hide a corrupted incidence index and allow the flip to commit after only local cavity validation.

Propagate stale-incidence failures
     if k_move >= 2
         && k_move < D
         && let Some(existing_simplex) =
-            find_simplex_containing_simplex(tds, inserted_face_vertices, removed_simplices)
+            find_simplex_containing_simplex(tds, inserted_face_vertices, removed_simplices)?
     {
 fn find_simplex_containing_simplex<U, V, const D: usize>(
     tds: &Tds<U, V, D>,
     simplex_vertices: &[VertexKey],
     removed_simplices: &[SimplexKey],
-) -> Option<SimplexKey>
+) -> Result<Option<SimplexKey>, FlipError>
 where
     U: DataType,
     V: DataType,
 {
-    let first = *simplex_vertices.first()?;
+    let Some(&first) = simplex_vertices.first() else {
+        return Ok(None);
+    };
 
     for simplex_key in tds.simplex_keys_containing_vertex(first) {
         if removed_simplices.contains(&simplex_key) {
             continue;
         }
 
-        let Some(simplex) = tds.simplex(simplex_key) else {
-            continue;
-        };
+        let simplex = tds
+            .simplex(simplex_key)
+            .ok_or(FlipError::MissingSimplex { simplex_key })?;
 
         if simplex_vertices
             .iter()
             .copied()
             .all(|vk| simplex.contains_vertex(vk))
         {
-            return Some(simplex_key);
+            return Ok(Some(simplex_key));
         }
     }
 
-    None
+    Ok(None)
 }

As per coding guidelines, “Every mutating operation MUST preserve invariants checked by Tds::is_valid (Level 1–3) and DelaunayTriangulation::is_valid (Level 4); operations that cannot preserve them must fail explicitly.”

Also applies to: 1150-1157

🤖 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/flips.rs` around lines 430 - 432, The current code uses
continue to skip when find_simplex_containing_simplex returns None, which can
hide corrupted incidence index violations. Instead of continuing, explicitly
surface the stale incidence error by returning an appropriate error result. This
ensures the operation fails when simplex_keys_containing_vertex encounters an
unresolved simplex key, preserving the invariant that all incidence indices must
be valid. Apply the same fix to the similar pattern mentioned at lines
1150-1157.

Source: Coding guidelines

🧹 Nitpick comments (1)
src/core/tds/equality.rs (1)

42-46: Consider using total_cmp for consistency with other coordinate comparisons in the codebase

The current implementation is safe due to NaN rejection at construction time (documented at line 56–57), making the None branch unreachable. However, other coordinate comparisons elsewhere (Point::cmp, Simplex::cmp, construction ordering) use total_cmp with explicit iteration. Adopting that pattern would improve consistency and align with established practice.

The claim about sign loss is incorrect: IEEE 754 specifies that ±0.0 are equal, which is the correct behavior for equality comparison. The panic risk from malformed coordinates is also overstated, since construction validates all coordinates.

🤖 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/tds/equality.rs` around lines 42 - 46, The compare_coords function
currently uses partial_cmp with a fallback to CmpOrdering::Equal for the None
case, but other coordinate comparisons in the codebase (such as Point::cmp and
Simplex::cmp) use total_cmp instead. Refactor the compare_coords function to use
total_cmp on the left and right coordinate arrays for consistency with the
established pattern elsewhere in the codebase. This aligns with how construction
ordering and other coordinate comparisons handle the same comparison logic.

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 `@docs/numerical_robustness_guide.md`:
- Line 335: The markdown documentation file has an incorrect file path reference
on line 335. The link currently points to `src/core/tds/storage.rs` but the
function `validate_simplex_coordinate_uniqueness` is actually defined in
`src/core/tds/validation.rs`. Update the link in the documentation to reference
the correct file path `src/core/tds/validation.rs` instead of the storage
module.

In `@docs/ORIENTATION_SPEC.md`:
- Line 235: In the documentation at line 235 of docs/ORIENTATION_SPEC.md, remove
the references to storage.rs and triangulation.rs from the list of files
containing orientation unit tests. Add src/core/tds/validation.rs to the list
instead, as this is where the actual coherent orientation tests are located,
including test_facet_permutation_parity_derives_coherent_and_incoherent_cases()
and test_orientation_validation_* functions.
- Line 27: The documentation file contains incorrect module references for the
method `Tds::is_coherently_oriented`. The method is actually located in
`src/core/tds/validation.rs`, not in `src/core/tds/storage.rs` as currently
documented. Update all references to this method in the ORIENTATION_SPEC.md file
to point to the correct validation module instead of the storage module. There
are two locations that need correction where the link references
`src/core/tds/storage.rs` - replace these with `src/core/tds/validation.rs` to
ensure the documentation links to the actual implementation.

---

Outside diff comments:
In `@docs/code_organization.md`:
- Around line 450-468: The documentation is missing the tds/validation.rs module
from the architecture inventory at lines 450-457, even though it is referenced
later at line 468 in the validation.rs section. Add a new entry for
tds/validation.rs to the TDS modules list after the tds/snapshot.rs entry with a
brief description indicating it handles Level 2 structural validation and
adjacency consistency checks. Then verify that the Level mapping comment at line
468 correctly reflects all documented modules, ensuring consistency between the
architecture overview and the validation orchestration description.

In `@src/core/algorithms/flips.rs`:
- Around line 430-432: The current code uses continue to skip when
find_simplex_containing_simplex returns None, which can hide corrupted incidence
index violations. Instead of continuing, explicitly surface the stale incidence
error by returning an appropriate error result. This ensures the operation fails
when simplex_keys_containing_vertex encounters an unresolved simplex key,
preserving the invariant that all incidence indices must be valid. Apply the
same fix to the similar pattern mentioned at lines 1150-1157.

In `@src/delaunay/construction.rs`:
- Around line 5128-5145: The is_non_retryable_construction_error function is
missing two deterministic structural failure variants that should be classified
as non-retryable. Add OrientationCanonicalizationInternal and
InsertionNeighborWiring to the pattern match within the matches! macro by
including two additional match arms (OrientationCanonicalizationInternal { .. }
and InsertionNeighborWiring { .. }) as DelaunayConstructionFailure variants in
the same list as the other non-retryable failure types, ensuring these failures
are not retried and their typed source is preserved.

---

Nitpick comments:
In `@src/core/tds/equality.rs`:
- Around line 42-46: The compare_coords function currently uses partial_cmp with
a fallback to CmpOrdering::Equal for the None case, but other coordinate
comparisons in the codebase (such as Point::cmp and Simplex::cmp) use total_cmp
instead. Refactor the compare_coords function to use total_cmp on the left and
right coordinate arrays for consistency with the established pattern elsewhere
in the codebase. This aligns with how construction ordering and other coordinate
comparisons handle the same comparison logic.
🪄 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: e8ce8901-0bea-4fa0-896e-2ccbab233dc5

📥 Commits

Reviewing files that changed from the base of the PR and between 1985e6f and 3d37be8.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (56)
  • .github/workflows/benchmarks.yml
  • .github/workflows/ci.yml
  • .github/workflows/generate-baseline.yml
  • .github/workflows/release-benchmarks.yml
  • .github/workflows/semgrep-sarif.yml
  • AGENTS.md
  • Cargo.toml
  • benches/README.md
  • benches/pl_manifold_repair.rs
  • docs/ORIENTATION_SPEC.md
  • docs/code_organization.md
  • docs/dev/commands.md
  • docs/dev/rust.md
  • docs/dev/testing.md
  • docs/dev/tooling-alignment.md
  • docs/invariants.md
  • docs/numerical_robustness_guide.md
  • docs/production_review_remediation_checklist.md
  • justfile
  • pyproject.toml
  • semgrep.yaml
  • src/bench_fixtures.rs
  • src/core/adjacency.rs
  • src/core/algorithms/flips.rs
  • src/core/algorithms/incremental_insertion.rs
  • src/core/algorithms/pl_manifold_repair.rs
  • src/core/construction.rs
  • src/core/insertion.rs
  • src/core/orientation.rs
  • src/core/query.rs
  • src/core/repair.rs
  • src/core/simplex.rs
  • src/core/tds.rs
  • src/core/tds/equality.rs
  • src/core/tds/errors.rs
  • src/core/tds/incidence.rs
  • src/core/tds/keys.rs
  • src/core/tds/mutation.rs
  • src/core/tds/snapshot.rs
  • src/core/tds/storage.rs
  • src/core/tds/validation.rs
  • src/delaunay/builder.rs
  • src/delaunay/construction.rs
  • src/delaunay/delaunayize.rs
  • src/delaunay/insertion.rs
  • src/delaunay/query.rs
  • src/lib.rs
  • src/topology/manifold.rs
  • tests/dedup_batch_construction.rs
  • tests/delaunay_edge_cases.rs
  • tests/large_scale_debug.rs
  • tests/prelude_exports.rs
  • tests/regressions.rs
  • tests/semgrep/src/project_rules/rust_style.rs
  • tests/trait_bound_ergonomics.rs
  • tests/triangulation_builder.rs

Comment thread docs/numerical_robustness_guide.md Outdated
Comment thread docs/ORIENTATION_SPEC.md Outdated
Comment thread docs/ORIENTATION_SPEC.md Outdated
@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.71313% with 397 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.52%. Comparing base (1985e6f) to head (82b3147).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/delaunay/builder.rs 28.28% 142 Missing ⚠️
src/delaunay/construction.rs 66.66% 134 Missing ⚠️
src/core/algorithms/incremental_insertion.rs 51.88% 51 Missing ⚠️
src/core/algorithms/flips.rs 87.86% 33 Missing ⚠️
src/core/insertion.rs 75.00% 9 Missing ⚠️
src/core/repair.rs 93.57% 9 Missing ⚠️
src/core/construction.rs 38.46% 8 Missing ⚠️
src/core/tds/equality.rs 98.55% 3 Missing ⚠️
src/core/tds/errors.rs 99.33% 3 Missing ⚠️
src/core/algorithms/pl_manifold_repair.rs 90.90% 2 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #473      +/-   ##
==========================================
- Coverage   91.65%   91.52%   -0.14%     
==========================================
  Files          72       77       +5     
  Lines       68792    69714     +922     
==========================================
+ Hits        63053    63804     +751     
- Misses       5739     5910     +171     
Flag Coverage Δ
unittests 91.52% <84.71%> (-0.14%) ⬇️

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.

@acgetchell acgetchell disabled auto-merge June 20, 2026 17:42
- Maintain vertex-to-simplices incidence as an explicit TDS index used by adjacency traversal, local repair, and stale-incidence detection.
- Add lifetime-bound `TriangulationAdjacency` views for repeated topology queries while keeping detached adjacency indexes checked.
- Surface dangling vertex incidence as typed flip and removal errors instead of silently skipping corrupted index entries.
- Refresh TDS module docs, prelude exports, and allocation benchmarks for the split incidence, mutation, and validation layout.

BREAKING CHANGE: `AdjacencyIndex` now borrows TDS incidence, and `*_with_index` query helpers require an index tied to the queried triangulation lifetime. Callers with repeated adjacency queries should use `adjacency()` / `TriangulationAdjacency` or rebuild an index from the same triangulation snapshot.

@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: 4

Caution

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

⚠️ Outside diff range comments (2)
src/delaunay/construction.rs (1)

3019-3021: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop retrying non-geometric Delaunay-validation failures.

candidate.is_delaunay_via_flips() errors are always stored as DelaunayValidation, but the non-retryable repair classification is only applied when the same DelaunayRepairError is wrapped by DelaunayRepair / InsertionDelaunayRepair. That means deterministic failures such as invalid topology or hard verification errors can still burn every shuffled retry and surface as ShuffledRetryExhausted instead of the immediate typed failure. Mirror is_geometric_repair_error in both retry paths before assigning last_failure.

Possible direction
+    fn map_delaunay_validation_retry_failure(
+        source: DelaunayRepairError,
+    ) -> Result<DelaunayConstructionRetryFailure, DelaunayTriangulationConstructionError> {
+        if is_geometric_repair_error(&source) {
+            Ok(DelaunayConstructionRetryFailure::DelaunayValidation {
+                source: Box::new(source),
+            })
+        } else {
+            Err(DelaunayTriangulationConstructionError::Triangulation(
+                DelaunayConstructionFailure::DelaunayRepair {
+                    phase: DelaunayConstructionRepairPhase::Completion,
+                    source: Box::new(source),
+                },
+            ))
+        }
+    }

Then use it at each candidate.is_delaunay_via_flips() error branch, wrapping the returned error with DelaunayTriangulationConstructionErrorWithStatistics in the statistics variant.

Also applies to: 3102-3107, 3236-3242, 3347-3354, 5155-5157

🤖 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/construction.rs` around lines 3019 - 3021, The issue is that
deterministic Delaunay-validation failures from
candidate.is_delaunay_via_flips() are being retried unnecessarily, burning all
shuffled retries before surfacing as ShuffledRetryExhausted instead of the
actual typed failure. You need to create a helper function that mirrors the
is_geometric_repair_error logic to classify whether a DelaunayRepairError is a
geometric error or a non-retryable deterministic failure. Then apply this
classification check at each candidate.is_delaunay_via_flips() error branch
(appearing at lines around 3019, 3102, 3236, 3347, and 5155) before assigning to
last_failure. When the error is non-geometric and non-retryable, wrap it with
DelaunayTriangulationConstructionErrorWithStatistics in the statistics variant
to ensure immediate failure instead of continued retry attempts.
src/core/tds/equality.rs (1)

95-124: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Compare canonical vertex incidence before returning equality.

This equality path can return true after matching vertices and simplex vertex sets, but it never checks the now-authoritative vertex→simplices incidence index. A TDS with stale or missing incidence entries can therefore compare equal to a valid TDS, masking corruption in the canonical representation. Normalize and compare each vertex’s incident simplex UUID set, or otherwise validate incidence consistency, before the final true.

Based on PR objectives, vertex incidence is now the canonical storage mechanism within the TDS.

🤖 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/tds/equality.rs` around lines 95 - 124, After the simplex comparison
loop in the triangulation equality check completes successfully (after iterating
through all simplices with eq_by_vertices), add validation of the vertex
incidence index before returning true. For each vertex in the triangulation,
normalize and compare the incident simplex UUID sets between self and other to
ensure the canonical vertex→simplices incidence representation is consistent
between both TDS instances. If any vertex's incident simplex set differs or is
missing, return false instead of allowing the equality check to proceed.
🧹 Nitpick comments (1)
src/delaunay/construction.rs (1)

5186-5196: ⚡ Quick win

Remove or preserve the dropped completion-repair message.

map_completion_repair_error now ignores _message, but both callers still allocate formatted context strings before calling it. Either remove the parameter and call-site format! work, or add typed context if the seeded/global failure detail is still needed.

Minimal cleanup if the typed source is sufficient
-    pub(crate) fn map_completion_repair_error(
-        _message: String,
-        repair_error: DelaunayRepairError,
-    ) -> DelaunayTriangulationConstructionError {
+    pub(crate) fn map_completion_repair_error(
+        repair_error: DelaunayRepairError,
+    ) -> DelaunayTriangulationConstructionError {
         DelaunayTriangulationConstructionError::Triangulation(
             DelaunayConstructionFailure::DelaunayRepair {
                 phase: DelaunayConstructionRepairPhase::Completion,
                 source: Box::new(repair_error),
             },
         )
     }

Call sites can then pass only the repair error instead of constructing an unused message.

Also applies to: 4526-4550

🤖 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/construction.rs` around lines 5186 - 5196, The
`map_completion_repair_error` function has an unused `_message` parameter
(indicated by the underscore prefix), yet callers are still allocating and
formatting context strings before calling it, which is wasteful. Remove the
`_message` parameter from the `map_completion_repair_error` function signature
entirely, and update all call sites (at lines 4526-4550 and others) to stop
constructing formatted messages and pass only the repair_error parameter to this
function.
🤖 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/core/adjacency.rs`:
- Around line 186-190: Add the #[must_use] attribute to the
TriangulationAdjacency struct definition. Since this is a public wrapper type
that represents a built adjacency view, it should have the #[must_use] macro
attribute placed alongside the existing #[derive(Debug)] and #[non_exhaustive]
attributes to ensure instances are not accidentally dropped without being used.

In `@src/core/algorithms/flips.rs`:
- Around line 15705-15754: Replace the individual dimension-specific test
functions find_simplex_containing_simplex_rejects_stale_incidence_key,
build_k1_inverse_context_rejects_stale_incidence_before_multiplicity,
build_k2_inverse_context_rejects_stale_incidence_key, and
build_k3_inverse_context_rejects_stale_incidence_key with pastey macro-generated
parametrized tests that cover the feasible dimension ranges: D=2 through D=5 for
the first two functions, D=3 through D=5 for the build_k2_flip_context_from_edge
test, and D=4 through D=5 for the build_k3_flip_context_from_triangle test. Use
the pastey macro syntax to iterate over these dimension ranges and generate test
variants for each dimension, ensuring the generic parameter in Tds and related
types reflects the current dimension being tested.
- Around line 1164-1174: The order of validation checks in the loop iterating
over simplex_keys_containing_vertex is incorrect. Currently, the code checks if
simplex_key is in removed_simplices before validating that the simplex actually
exists via tds.simplex(simplex_key). This allows stale incidence entries that
are also in the removal set to be silently skipped instead of triggering
FlipError::DanglingVertexIncidence. Reorder the checks so that
tds.simplex(simplex_key) is called and validated first to ensure dangling vertex
incidences are always detected, and only then check if the simplex_key should be
skipped because it exists in removed_simplices.

In `@src/core/tds/equality.rs`:
- Around line 157-210: The three new test functions
test_tds_partial_eq_different_counts_not_equal,
test_tds_partial_eq_rejects_dangling_simplex_vertex_key, and
test_tds_partial_eq_rejects_misaligned_periodic_offsets are currently hardcoded
to dimension D=2. Refactor these tests to be dimension-generic by wrapping them
with pastey macros to generate parameterized versions that cover dimensions D=2
through D=5, following the project's coding guidelines for dimension-generic
testing.

---

Outside diff comments:
In `@src/core/tds/equality.rs`:
- Around line 95-124: After the simplex comparison loop in the triangulation
equality check completes successfully (after iterating through all simplices
with eq_by_vertices), add validation of the vertex incidence index before
returning true. For each vertex in the triangulation, normalize and compare the
incident simplex UUID sets between self and other to ensure the canonical
vertex→simplices incidence representation is consistent between both TDS
instances. If any vertex's incident simplex set differs or is missing, return
false instead of allowing the equality check to proceed.

In `@src/delaunay/construction.rs`:
- Around line 3019-3021: The issue is that deterministic Delaunay-validation
failures from candidate.is_delaunay_via_flips() are being retried unnecessarily,
burning all shuffled retries before surfacing as ShuffledRetryExhausted instead
of the actual typed failure. You need to create a helper function that mirrors
the is_geometric_repair_error logic to classify whether a DelaunayRepairError is
a geometric error or a non-retryable deterministic failure. Then apply this
classification check at each candidate.is_delaunay_via_flips() error branch
(appearing at lines around 3019, 3102, 3236, 3347, and 5155) before assigning to
last_failure. When the error is non-geometric and non-retryable, wrap it with
DelaunayTriangulationConstructionErrorWithStatistics in the statistics variant
to ensure immediate failure instead of continued retry attempts.

---

Nitpick comments:
In `@src/delaunay/construction.rs`:
- Around line 5186-5196: The `map_completion_repair_error` function has an
unused `_message` parameter (indicated by the underscore prefix), yet callers
are still allocating and formatting context strings before calling it, which is
wasteful. Remove the `_message` parameter from the `map_completion_repair_error`
function signature entirely, and update all call sites (at lines 4526-4550 and
others) to stop constructing formatted messages and pass only the repair_error
parameter to this function.
🪄 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: cb76ec31-f3b7-4e9a-9bf9-205d093cc4bb

📥 Commits

Reviewing files that changed from the base of the PR and between 3d37be8 and a0e1265.

📒 Files selected for processing (23)
  • benches/allocation_hot_paths.rs
  • docs/ORIENTATION_SPEC.md
  • docs/code_organization.md
  • docs/dev/rust.md
  • docs/invariants.md
  • docs/numerical_robustness_guide.md
  • docs/production_review_remediation_checklist.md
  • src/core/adjacency.rs
  • src/core/algorithms/flips.rs
  • src/core/algorithms/incremental_insertion.rs
  • src/core/insertion.rs
  • src/core/query.rs
  • src/core/repair.rs
  • src/core/tds/equality.rs
  • src/core/tds/errors.rs
  • src/core/tds/incidence.rs
  • src/core/tds/mutation.rs
  • src/core/tds/storage.rs
  • src/delaunay/construction.rs
  • src/delaunay/flips.rs
  • src/delaunay/query.rs
  • src/lib.rs
  • tests/prelude_exports.rs
✅ Files skipped from review due to trivial changes (7)
  • src/delaunay/flips.rs
  • docs/invariants.md
  • docs/production_review_remediation_checklist.md
  • docs/ORIENTATION_SPEC.md
  • docs/numerical_robustness_guide.md
  • docs/code_organization.md
  • docs/dev/rust.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/core/tds/incidence.rs
  • tests/prelude_exports.rs
  • src/lib.rs
  • src/core/tds/errors.rs
  • src/core/algorithms/incremental_insertion.rs
  • src/core/query.rs

Comment thread src/core/adjacency.rs Outdated
Comment thread src/core/algorithms/flips.rs
Comment thread src/core/algorithms/flips.rs Outdated
Comment thread src/core/tds/equality.rs Outdated
@acgetchell acgetchell enabled auto-merge (squash) June 20, 2026 22:57
- Replace detached adjacency indexes with IncidenceView, EdgeIndex,
  SimplexNeighborIndex, and the composite TriangulationAdjacency view.
- Tie query indexes to the triangulation lifetime and remove stale
  *_with_index query helpers in favor of direct split topology queries.
- Canonicalize vertex incidence mutation with transactional removal,
  rollback reuse, typed stale-incidence errors, and incidence-aware equality.
- Preserve deterministic construction and Delaunay validation failures as
  typed errors instead of retry-exhaustion wrappers.

BREAKING CHANGE: `AdjacencyIndex`, `AdjacencyIndexBuildError`,
`build_adjacency_index()`, and the `*_with_index` query helpers have been
removed. Use `incidence()`, `build_edge_index()`,
`build_simplex_neighbor_index()`, or `adjacency()` with
`TopologyIndexBuildError` instead.

@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/core/tds/equality.rs (1)

169-174: ⚠️ Potential issue | 🟡 Minor

Don't mark Tds as Eq while PartialEq can be non-reflexive.

The eq method returns false when simplex_uuid_sort_entries returns None (line 173). The test-only helpers push_first_simplex_vertex_key_storage_only_for_test and set_first_simplex_periodic_offsets_storage_only_for_test deliberately create malformed TDS states with mismatched periodic offset counts, triggering this path. This violates the Eq trait's reflexivity contract (x == x must always be true), even though the malformed states are test-only constructions.

Either remove the Eq marker impl or ensure the sort entry path cannot fail for any reachable state, including intentionally corrupted test values.

🤖 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/tds/equality.rs` around lines 169 - 174, The Tds type is marked with
the Eq trait, but the PartialEq implementation in the eq method can violate
reflexivity by returning false when simplex_uuid_sort_entries returns None. This
occurs because test-only helpers like
push_first_simplex_vertex_key_storage_only_for_test and
set_first_simplex_periodic_offsets_storage_only_for_test create malformed TDS
states. Fix this by either removing the Eq marker from the Tds impl block
entirely, or alternatively by modifying the logic to ensure
simplex_uuid_sort_entries cannot fail for any reachable state, including the
test-corrupted values.
🤖 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/core/query.rs`:
- Around line 956-986: The test function
split_topology_indexes_cover_independent_query_families() currently only covers
dimension D=3, but the tested functions (incidence, build_edge_index,
build_simplex_neighbor_index) are const-generic across dimensions. Wrap this
test with pastey macros to generate dimension-generic test coverage for D=2
through D=5, following the same pattern used for accessor tests in the same
file. Apply the same treatment to the other split-index topology test cases
referenced in lines 1056-1098 and 1336-1428.

---

Outside diff comments:
In `@src/core/tds/equality.rs`:
- Around line 169-174: The Tds type is marked with the Eq trait, but the
PartialEq implementation in the eq method can violate reflexivity by returning
false when simplex_uuid_sort_entries returns None. This occurs because test-only
helpers like push_first_simplex_vertex_key_storage_only_for_test and
set_first_simplex_periodic_offsets_storage_only_for_test create malformed TDS
states. Fix this by either removing the Eq marker from the Tds impl block
entirely, or alternatively by modifying the logic to ensure
simplex_uuid_sort_entries cannot fail for any reachable state, including the
test-corrupted values.
🪄 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: 86c7ef49-7ea6-4ea9-af29-bec798754838

📥 Commits

Reviewing files that changed from the base of the PR and between a0e1265 and 83b470c.

📒 Files selected for processing (19)
  • AGENTS.md
  • docs/code_organization.md
  • docs/dev/rust.md
  • examples/triangulation_and_hull.rs
  • src/core/adjacency.rs
  • src/core/algorithms/flips.rs
  • src/core/algorithms/incremental_insertion.rs
  • src/core/query.rs
  • src/core/tds/equality.rs
  • src/core/tds/errors.rs
  • src/core/tds/mutation.rs
  • src/core/tds/storage.rs
  • src/delaunay/construction.rs
  • src/delaunay/query.rs
  • src/lib.rs
  • tests/example_workflows.rs
  • tests/prelude_exports.rs
  • tests/public_topology_api.rs
  • tests/trait_bound_ergonomics.rs
✅ Files skipped from review due to trivial changes (3)
  • AGENTS.md
  • docs/dev/rust.md
  • docs/code_organization.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/prelude_exports.rs
  • src/core/tds/errors.rs
  • src/lib.rs
  • src/delaunay/query.rs
  • src/delaunay/construction.rs

Comment thread src/core/query.rs Outdated
- Remove the Eq marker from Tds because malformed internal states can make
  semantic PartialEq comparisons non-reflexive.
- Expand split topology index coverage across D=2 through D=5 with a generic
  shared-facet fixture independent of Delaunay construction tie-breaking.

BREAKING CHANGE: `Tds<U, V, D>` no longer implements `Eq`. Use `PartialEq`
for semantic triangulation comparison; identity/provenance comparisons should
use an explicitly named API if needed.

@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

🤖 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/core/query.rs`:
- Around line 789-792: The fixture currently only sets the neighbor relationship
in one direction (positive_simplex_key to negative_simplex_key) but the tests
expect bidirectional neighbor relationships. After the existing
tds.set_neighbors_by_key call for positive_simplex_key, add a reciprocal call
that sets the neighbor relationship from negative_simplex_key back to
positive_simplex_key using the same pattern, ensuring both simplices have
symmetric neighbor assignments without relying on undocumented setter side
effects.
🪄 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: 6a387d3f-3eb7-4bf7-9c3e-07a51a22a39c

📥 Commits

Reviewing files that changed from the base of the PR and between 83b470c and e6e049e.

📒 Files selected for processing (2)
  • src/core/query.rs
  • src/core/tds/equality.rs
💤 Files with no reviewable changes (1)
  • src/core/tds/equality.rs

Comment thread src/core/query.rs
@acgetchell acgetchell merged commit 9a973df into main Jun 21, 2026
24 checks passed
@acgetchell acgetchell deleted the refactor/403-tds-incidence branch June 21, 2026 01:52
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.

Refactor TDS internals into smaller modules

1 participant