Skip to content

perf: cadence batch repair for large construction#369

Draft
acgetchell wants to merge 9 commits intomainfrom
perf/341-batch-repair-telemetry
Draft

perf: cadence batch repair for large construction#369
acgetchell wants to merge 9 commits intomainfrom
perf/341-batch-repair-telemetry

Conversation

@acgetchell
Copy link
Copy Markdown
Owner

  • Add a batch repair policy option to ConstructionOptions so bulk insertion can repair local Delaunay fronts every N insertions.
  • Carry pending local repair seeds through batch construction and finalization so cadenced repairs cover the accumulated frontier.
  • Report construction telemetry for locate walks, conflict regions, global exterior scans, skipped-vertex budgets, and initial-simplex A/B runs in the large-scale debug harness.

- Add a batch repair policy option to ConstructionOptions so bulk insertion can repair local Delaunay fronts every N insertions.
- Carry pending local repair seeds through batch construction and finalization so cadenced repairs cover the accumulated frontier.
- Report construction telemetry for locate walks, conflict regions, global exterior scans, skipped-vertex budgets, and initial-simplex A/B runs in the large-scale debug harness.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

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: 42d6bc44-1626-4c25-b5a2-ae09a23b8208

📥 Commits

Reviewing files that changed from the base of the PR and between e5c3d5c and a3ce13c.

📒 Files selected for processing (11)
  • benches/ci_performance_suite.rs
  • benches/topology_guarantee_construction.rs
  • examples/delaunayize_repair.rs
  • examples/numerical_robustness.rs
  • src/core/algorithms/locate.rs
  • src/core/triangulation.rs
  • tests/check_perturbation_stats.rs
  • tests/delaunay_incremental_insertion.rs
  • tests/euler_characteristic.rs
  • tests/large_scale_debug.rs
  • tests/proptest_triangulation.rs
✅ Files skipped from review due to trivial changes (4)
  • tests/check_perturbation_stats.rs
  • examples/numerical_robustness.rs
  • examples/delaunayize_repair.rs
  • benches/topology_guarantee_construction.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/euler_characteristic.rs
  • tests/delaunay_incremental_insertion.rs
  • tests/proptest_triangulation.rs
  • benches/ci_performance_suite.rs
  • src/core/triangulation.rs
  • src/core/algorithms/locate.rs
  • tests/large_scale_debug.rs

Walkthrough

Adds insertion telemetry and locate tracing; threads telemetry through insertion transactions; introduces pending-repair seeds with cadence-driven local repair; adds locality helpers and ValidationCadence; reorganizes triangulation facade and preludes; extends debug harness with new env vars, telemetry reporting, and tests.

Changes

Telemetry Collection & Batch Repair Integration

Layer / File(s) Summary
Telemetry Data Types
src/core/operations.rs
InsertionTelemetry aggregates locate calls, walk steps, conflict-region counts, cavity/hull/validation timings, and exterior-scan stats; ConstructionTelemetry and ConstructionStatistics.telemetry added.
Locate Trace / Diagnostics
src/core/algorithms/locate.rs
Adds LocateTrace { result, stats, terminal_cell }; locate_with_trace returns the trace; locate_with_stats delegates to the trace.
Insertion Pipeline Telemetry
src/core/triangulation.rs
DetailedInsertionResult gains telemetry; insertion pipeline initializes and threads &mut InsertionTelemetry through topology-safety-net, fallbacks, and try_insert_impl; adds helpers for recording locate/conflict/cavity/hull/validation timings and avoids global conflict scans on exterior hull-extension.
Locality Helpers
src/triangulation/locality.rs
Adds LocalConflictSeedCells, accumulate_live_cell_seeds, append_live_unique_cell_seeds, retain_live_cell_seeds, clear_cell_seed_set, retain_cells_and_record_removed, replace_cells_and_record_removed, and collect_local_exterior_conflict_seed_cells with unit tests.
Validation Cadence
src/triangulation/validation.rs
Adds ValidationCadence with from_optional_every and should_validate to drive incremental validation scheduling; unit tests included.
Construction Options & Telemetry
src/triangulation/delaunay.rs
Adds batch_repair_policy to ConstructionOptions; introduces ConstructionTelemetry; embeds telemetry in ConstructionStatistics and merges telemetry across retries; adds builder/getter and tests.
Batch Repair Cadence
src/triangulation/delaunay.rs
Seeded batch insertion accumulates pending_repair_seeds; repair_pending_local_seed_cells performs cadence-driven local flip repair with budget and escalation semantics; finalize_bulk_construction merges pending+soft_fail seeds and runs completion repair per policy.
Triangulation Facade & Preludes
src/triangulation.rs, src/lib.rs
Switches from inline triangulation module to external facade src/triangulation.rs; adds focused prelude::triangulation::construction and prelude::triangulation::validation exports; updates docs/examples/benches/tests to use narrower imports.
Debug Harness & Docs
tests/large_scale_debug.rs, docs/dev/debug_env_vars.md, justfile
Harness parses DELAUNAY_LARGE_DEBUG_INITIAL_SIMPLEX, DELAUNAY_LARGE_DEBUG_REPAIR_EVERY (default 2), DELAUNAY_LARGE_DEBUG_MAX_SKIP_PCT; derives topology and repair policy; records ConstructionTelemetry and slow_insertions; prints construction telemetry; enforces skip-percentage budget; extends tracing allowlist; docs and justfile updated.
Imports updates across tests/benches/examples/docs
many files
Replaces broad delaunay::prelude::triangulation::* wildcard imports with explicit prelude::triangulation::construction, ::validation, or ::flips imports across tests, benches, examples, and docs snippets.

Sequence Diagram(s)

(omitted — change is broad and multi-part; sequence diagrams not generated)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I counted locates, steps, and scans with care,

I queued the seeds and let the repairs wait,
Timers whispered nanos into the log,
Cadence hummed while flips resolved their state,
A rabbit smiled — telemetry is great!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding batch repair cadence capability during large-scale Delaunay triangulation construction.
Description check ✅ Passed The description is directly related to the changeset, detailing the three core objectives: batch repair policy, pending seed tracking, and telemetry reporting.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 perf/341-batch-repair-telemetry

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 9, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 8 complexity

Metric Results
Complexity 8

View in Codacy

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 documentation Improvements or additions to documentation enhancement New feature or request rust Pull requests that update rust code geometry Geometry-related issues api topology labels May 9, 2026
Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (4)
src/core/operations.rs (1)

233-274: 💤 Low value

Consider using u64 for nanosecond timing.

The global_conflict_scan_nanos field uses u128, but the documentation states this struct captures telemetry for "one transactional insertion." For single-insertion timing, u64 provides ample range (~584 years of nanoseconds) and matches std::time::Duration's internal representation. Using u64 would be simpler and save 8 bytes per instance.

♻️ Suggested change
-    /// Wall-clock nanoseconds spent in global exterior-point conflict scans.
-    pub global_conflict_scan_nanos: u128,
+    /// Wall-clock nanoseconds spent in global exterior-point conflict scans.
+    pub global_conflict_scan_nanos: u64,
🤖 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/operations.rs` around lines 233 - 274, The field
global_conflict_scan_nanos in struct InsertionTelemetry is using u128 but should
be u64 to match Duration semantics and save space; change the type of
InsertionTelemetry::global_conflict_scan_nanos from u128 to u64, update any code
that assigns, accumulates, or converts this field (search for references to
InsertionTelemetry and global_conflict_scan_nanos, as well as any
add/extend/Duration::as_nanos() uses) to cast or use as u64 safely, and update
any tests or Default/serialization code that assume u128 accordingly.
src/triangulation/delaunay.rs (1)

7956-7969: ⚡ Quick win

Add one end-to-end test for the new EveryN cadence path.

The new tests only validate the builder/getter. They do not exercise the carried-frontier behavior in pending_repair_seeds/finalize_bulk_construction, so a regression in the actual cadenced repair path would still pass.

As per coding guidelines, "Unit tests must cover known values, error paths, and dimension-generic correctness."

🤖 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/triangulation/delaunay.rs` around lines 7956 - 7969, The test currently
only verifies ConstructionOptions getters; add an end-to-end unit test (or
extend test_construction_options_builder_roundtrip) that uses
ConstructionOptions::with_batch_repair_policy(DelaunayRepairPolicy::EveryN(...))
and drives a bulk insertion path so the cadenced repair branch is executed:
construct a builder/triangulation, call the bulk insertion routine that leads to
pending_repair_seeds and then call finalize_bulk_construction, and assert that
repairs occurred at the expected cadence (e.g., by checking pending_repair_seeds
is drained, internal repair count/state, or final triangulation invariants).
Ensure the test references ConstructionOptions, DelaunayRepairPolicy::EveryN,
pending_repair_seeds, and finalize_bulk_construction so the EveryN path is
actually exercised rather than only testing getters.
tests/large_scale_debug.rs (2)

1180-1206: ⚡ Quick win

Add a total == 0 case to the skip-percentage test.

skip_percentage(x, 0) is the one branch with explicit short-circuit logic and is currently unexercised by test_skip_percentage_reports_ratio. A single extra assertion locks in the divide-by-zero contract.

🧪 Suggested addition
 #[test]
 fn test_skip_percentage_reports_ratio() {
     assert!((skip_percentage(0, 100) - 0.0).abs() < f64::EPSILON);
     assert!((skip_percentage(4, 400) - 1.0).abs() < f64::EPSILON);
     assert!((skip_percentage(12, 100) - 12.0).abs() < f64::EPSILON);
+    // total == 0 must short-circuit and not divide by zero.
+    assert!((skip_percentage(0, 0) - 0.0).abs() < f64::EPSILON);
+    assert!((skip_percentage(5, 0) - 0.0).abs() < f64::EPSILON);
 }

Optional: test_initial_simplex_strategy_from_name_maps_ab_switch also exercises initial_simplex_strategy_name at line 1196 — splitting into two tests would make failures point at the right helper, but that's purely cosmetic.

🤖 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/large_scale_debug.rs` around lines 1180 - 1206, Add an assertion to
test_skip_percentage_reports_ratio that exercises the total == 0 short-circuit
in skip_percentage; e.g., assert that skip_percentage(0, 0) (or
skip_percentage(any, 0)) equals 0.0 using the same f64::EPSILON comparison
pattern so the divide-by-zero branch is covered.

346-377: 💤 Low value

The #[non_exhaustive] enum makes the wildcard in initial_simplex_strategy_name necessary but suggests adding logging when unknown variants are encountered.

InitialSimplexStrategy is marked #[non_exhaustive], so the wildcard arm is not dead code—it will handle future variants. However, a debug harness should surface when unknown variants appear. Consider adding a debug! or warn! tracing statement in the wildcard arm to catch future additions early:

const fn initial_simplex_strategy_name(strategy: InitialSimplexStrategy) -> &'static str {
    match strategy {
        InitialSimplexStrategy::First => "first",
        InitialSimplexStrategy::Balanced => "balanced",
        _ => {
            debug!("unknown InitialSimplexStrategy variant");
            "unknown"
        }
    }
}

The asymmetry across the three helpers is reasonable: name() converts to a display string, from_name() parses user input with None fallback, and from_env() panics on invalid configuration. Explicit logging makes the behavior intentional and catches variants that slip through.

🤖 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/large_scale_debug.rs` around lines 346 - 377, The wildcard arm in
initial_simplex_strategy_name should log unexpected #[non_exhaustive] variants
so future additions are visible; change initial_simplex_strategy_name from const
fn to a normal fn (so logging macros can be used) and add a tracing::debug! (or
tracing::warn!) call in the `_` arm that includes the strategy (or its
discriminant/string) before returning "unknown"; keep
initial_simplex_strategy_from_name and initial_simplex_strategy_from_env
behavior unchanged.
🤖 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/triangulation.rs`:
- Around line 118-120: The import list brings in locate but it's only used in a
debug-only block; wrap the locate import with a debug_assertions cfg so release
builds don't show it as unused (e.g., conditionally import locate when
cfg(debug_assertions)). Keep the other imports (ConflictError, LocateError,
LocateResult, LocateStats, extract_cavity_boundary, find_conflict_region,
locate_by_scan, locate_with_stats) unchanged and only apply the cfg to the
locate symbol referenced in the debug block around the locate usage.
- Around line 5213-5245: The locate_by_scan() retry path currently bypasses the
shared telemetry updater, so increment the scan-fallback counter and record the
locate outcome there: in the code path that calls locate_by_scan() as a retry
(the fallback branch that invokes locate_by_scan()), add
telemetry.locate_scan_fallbacks =
telemetry.locate_scan_fallbacks.saturating_add(1) and then call the common
recorder (e.g., record_locate_telemetry or the helper used elsewhere such as
record_locate_outcome) with the LocateResult and LocateStats from that retry so
locate_calls, locate_walk_steps_*, locate_hint_uses and located_* are accounted
for the scan-retry case as well. Ensure you pass the same InsertionTelemetry,
LocateResult and LocateStats types used by record_locate_telemetry.

In `@src/triangulation/delaunay.rs`:
- Around line 4706-4718: The current match always maps any error from
repair_delaunay_local_single_pass into
TriangulationConstructionError::GeometricDegeneracy; instead, preserve and
propagate internal repair errors untouched so non-retryable failures fail fast.
Update the match on repair_result to: on Ok(()) return Ok(()); on Err(e) if e is
already a DelaunayTriangulationConstructionError (or can be converted from the
underlying repair error) propagate/return that error directly; otherwise wrap
only true geometric degeneracy cases in
TriangulationConstructionError::GeometricDegeneracy as before. Refer to
repair_result, repair_delaunay_local_single_pass,
DelaunayTriangulationConstructionError, and
TriangulationConstructionError::GeometricDegeneracy to locate and implement the
change.

---

Nitpick comments:
In `@src/core/operations.rs`:
- Around line 233-274: The field global_conflict_scan_nanos in struct
InsertionTelemetry is using u128 but should be u64 to match Duration semantics
and save space; change the type of
InsertionTelemetry::global_conflict_scan_nanos from u128 to u64, update any code
that assigns, accumulates, or converts this field (search for references to
InsertionTelemetry and global_conflict_scan_nanos, as well as any
add/extend/Duration::as_nanos() uses) to cast or use as u64 safely, and update
any tests or Default/serialization code that assume u128 accordingly.

In `@src/triangulation/delaunay.rs`:
- Around line 7956-7969: The test currently only verifies ConstructionOptions
getters; add an end-to-end unit test (or extend
test_construction_options_builder_roundtrip) that uses
ConstructionOptions::with_batch_repair_policy(DelaunayRepairPolicy::EveryN(...))
and drives a bulk insertion path so the cadenced repair branch is executed:
construct a builder/triangulation, call the bulk insertion routine that leads to
pending_repair_seeds and then call finalize_bulk_construction, and assert that
repairs occurred at the expected cadence (e.g., by checking pending_repair_seeds
is drained, internal repair count/state, or final triangulation invariants).
Ensure the test references ConstructionOptions, DelaunayRepairPolicy::EveryN,
pending_repair_seeds, and finalize_bulk_construction so the EveryN path is
actually exercised rather than only testing getters.

In `@tests/large_scale_debug.rs`:
- Around line 1180-1206: Add an assertion to test_skip_percentage_reports_ratio
that exercises the total == 0 short-circuit in skip_percentage; e.g., assert
that skip_percentage(0, 0) (or skip_percentage(any, 0)) equals 0.0 using the
same f64::EPSILON comparison pattern so the divide-by-zero branch is covered.
- Around line 346-377: The wildcard arm in initial_simplex_strategy_name should
log unexpected #[non_exhaustive] variants so future additions are visible;
change initial_simplex_strategy_name from const fn to a normal fn (so logging
macros can be used) and add a tracing::debug! (or tracing::warn!) call in the
`_` arm that includes the strategy (or its discriminant/string) before returning
"unknown"; keep initial_simplex_strategy_from_name and
initial_simplex_strategy_from_env behavior unchanged.
🪄 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: 73680a4b-9c5d-417e-8155-af53bb38dec9

📥 Commits

Reviewing files that changed from the base of the PR and between aa0de2c and d3b35cc.

📒 Files selected for processing (5)
  • docs/dev/debug_env_vars.md
  • src/core/operations.rs
  • src/core/triangulation.rs
  • src/triangulation/delaunay.rs
  • tests/large_scale_debug.rs

Comment thread src/core/triangulation.rs Outdated
Comment thread src/core/triangulation.rs
Comment thread src/triangulation/delaunay.rs Outdated
- Return live insertion-created cells as repair seeds so batch construction can
  accumulate local frontiers without scanning vertex stars globally.
- Move repair seed frontier helpers into triangulation locality utilities and
  defer broad repair fallback to final construction repair.
- Extend construction telemetry and large-scale debug output with seed
  accumulation timing.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

❌ Patch coverage is 91.15854% with 58 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.78%. Comparing base (aa0de2c) to head (e5c3d5c).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/core/triangulation.rs 85.79% 48 Missing ⚠️
src/core/algorithms/locate.rs 84.84% 5 Missing ⚠️
src/triangulation/locality.rs 98.39% 4 Missing ⚠️
src/triangulation/validation.rs 96.87% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #369      +/-   ##
==========================================
+ Coverage   89.66%   89.78%   +0.12%     
==========================================
  Files          58       60       +2     
  Lines       53693    54835    +1142     
==========================================
+ Hits        48144    49236    +1092     
- Misses       5549     5599      +50     
Flag Coverage Δ
unittests 89.78% <91.15%> (+0.12%) ⬆️

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.

- Preserve the terminal locate cell so exterior insertion can seed nearby repair work without scanning the full triangulation.
- Skip global conflict-region scans during exterior hull extension and defer broad Delaunay cleanup to cadenced and final repair.
- Move local repair seed bookkeeping helpers into triangulation locality utilities.
Copy link
Copy Markdown
Contributor

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

Caution

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

⚠️ Outside diff range comments (1)
src/core/triangulation.rs (1)

5477-5528: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize Outside + Some(empty) to the same seed path as Outside + None.

Both branches fall through to hull extension, but only the None case gathers terminal_cell-based repair_seed_cells. If an upstream caller materializes “no conflict region” as Some(&empty_buf), cadenced repair loses the local frontier entirely, so behavior now depends on how the caller represents the same state. Reuse the None path for empty buffers before building InsertionSite::Exterior.

🤖 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/triangulation.rs` around lines 5477 - 5528, The Outside+Some(empty)
path is currently treated differently from Outside+None and loses the
terminal_cell-based repair_seed_cells; modify the (LocateResult::Outside,
Some(cells)) branch to normalize empty buffers by computing repair_seed_cells
the same way as the None branch: if cells.is_empty() then run the same
conditional that checks !locate_stats.fell_back_to_scan() &&
self.tds.contains_cell(locate_trace.terminal_cell) and call
collect_local_exterior_conflict_seed_cells(&self.tds, &self.kernel, &point,
locate_trace.terminal_cell) to produce repair_seed_cells (or
CellKeyBuffer::new() otherwise), then build InsertionSite::Exterior {
conflict_cells: None, repair_seed_cells } so both empty Some and None follow the
identical seed-path; reference LocateResult::Outside,
locate_trace.terminal_cell, collect_local_exterior_conflict_seed_cells,
InsertionSite::Exterior, repair_seed_cells, and CellKeyBuffer.
🧹 Nitpick comments (1)
src/triangulation/locality.rs (1)

51-68: ⚡ Quick win

Avoid quadratic dedupe in append_live_unique_cell_seeds for large batches.

Line 62 performs a linear contains check per candidate. In cadence/batch paths this can become a hotspot; build one set from existing seeds and use O(1) inserts.

⚡ Proposed refactor
 pub fn append_live_unique_cell_seeds<T, U, V, const D: usize>(
     tds: &Tds<T, U, V, D>,
     candidate_seed_cells: &[CellKey],
     seed_cells: &mut CellKeyBuffer,
 ) -> usize
 where
     U: DataType,
     V: DataType,
 {
     let mut added = 0usize;
+    let mut seen: FastHashSet<CellKey> = seed_cells.iter().copied().collect();
     for &cell_key in candidate_seed_cells {
-        if tds.contains_cell(cell_key) && !seed_cells.contains(&cell_key) {
+        if tds.contains_cell(cell_key) && seen.insert(cell_key) {
             seed_cells.push(cell_key);
             added = added.saturating_add(1);
         }
     }
     added
 }
🤖 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/triangulation/locality.rs` around lines 51 - 68,
append_live_unique_cell_seeds currently does a linear seed_cells.contains check
per candidate, causing O(n*m) behavior; change it to build a HashSet (or similar
O(1) lookup) from the existing seed_cells once, then iterate
candidate_seed_cells and for each cell_key call tds.contains_cell(cell_key) and
check membership in that set, inserting into both the set and seed_cells
(CellKeyBuffer) when new; also consider reserving capacity in the set to avoid
rehashes. Use the function and variable names append_live_unique_cell_seeds,
seed_cells, candidate_seed_cells, CellKeyBuffer, CellKey, and tds.contains_cell
to locate and update the code.
🤖 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/triangulation.rs`:
- Around line 4233-4239: The counter/telemetry is being recorded even when
validate_after_insertion(...) only gates and skips real checks; change the
call/site so telemetry (Self::record_topology_validation_telemetry and
incrementing topology_validation_calls) is only invoked when actual validation
work runs. Do this by having validate_after_insertion return a discriminant
(e.g., ValidationOutcome or (bool did_run, Result<(),E>)) or expose a helper
like validation_will_run(...), then: call Instant::now() and record
duration/metric only when did_run is true (i.e., real Level3/link checks
executed). Apply the same change to the other occurrence mentioned (the block
around 4289-4295) so skipped-policy invocations do not increment the validation
counter.

In `@src/triangulation/locality.rs`:
- Around line 18-23: The public wrapper struct LocalConflictSeedCells can be
silently discarded; add the #[must_use] attribute to the LocalConflictSeedCells
declaration so callers get a compiler warning if they ignore the result (leave
the public fields seed_cells: CellKeyBuffer and conflict_cells_found: usize
unchanged). Locate the struct named LocalConflictSeedCells and prepend
#[must_use] directly above its declaration.

---

Outside diff comments:
In `@src/core/triangulation.rs`:
- Around line 5477-5528: The Outside+Some(empty) path is currently treated
differently from Outside+None and loses the terminal_cell-based
repair_seed_cells; modify the (LocateResult::Outside, Some(cells)) branch to
normalize empty buffers by computing repair_seed_cells the same way as the None
branch: if cells.is_empty() then run the same conditional that checks
!locate_stats.fell_back_to_scan() &&
self.tds.contains_cell(locate_trace.terminal_cell) and call
collect_local_exterior_conflict_seed_cells(&self.tds, &self.kernel, &point,
locate_trace.terminal_cell) to produce repair_seed_cells (or
CellKeyBuffer::new() otherwise), then build InsertionSite::Exterior {
conflict_cells: None, repair_seed_cells } so both empty Some and None follow the
identical seed-path; reference LocateResult::Outside,
locate_trace.terminal_cell, collect_local_exterior_conflict_seed_cells,
InsertionSite::Exterior, repair_seed_cells, and CellKeyBuffer.

---

Nitpick comments:
In `@src/triangulation/locality.rs`:
- Around line 51-68: append_live_unique_cell_seeds currently does a linear
seed_cells.contains check per candidate, causing O(n*m) behavior; change it to
build a HashSet (or similar O(1) lookup) from the existing seed_cells once, then
iterate candidate_seed_cells and for each cell_key call
tds.contains_cell(cell_key) and check membership in that set, inserting into
both the set and seed_cells (CellKeyBuffer) when new; also consider reserving
capacity in the set to avoid rehashes. Use the function and variable names
append_live_unique_cell_seeds, seed_cells, candidate_seed_cells, CellKeyBuffer,
CellKey, and tds.contains_cell to locate and update the code.
🪄 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: 4f67cbdc-85d6-4edc-83ac-4fbec016c860

📥 Commits

Reviewing files that changed from the base of the PR and between d06e498 and cbb06bd.

📒 Files selected for processing (3)
  • src/core/algorithms/locate.rs
  • src/core/triangulation.rs
  • src/triangulation/locality.rs

Comment thread src/core/triangulation.rs Outdated
Comment thread src/triangulation/locality.rs
- Keep exterior repair seeding local when conflict buffers are empty, avoiding global conflict-region scans during insertion.
- Gate topology-validation telemetry on actual validation work and add typed validation cadences for large-scale diagnostics.
- Preserve typed Delaunay repair failures with construction repair phases and keep mutation repair gates independent of insertion cadence.
- Add focused triangulation construction, repair, and validation preludes across docs, examples, benches, and tests.
- Add an adaptive seed-backlog trigger for batch local Delaunay repair.
- Track local repair frontier sizes and cadence versus backlog triggers.
- Expose the 3D large-scale repair interval in the debug just recipe.
- Clarify batch repair policy docs and cover trigger/telemetry behavior with focused tests.
Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
justfile (1)

236-237: ⚡ Quick win

Align repair_every default with harness/docs default to avoid drift.

Line 236 and Line 284 default to repair_every=2; if the large-scale harness default is 4, this recipe silently changes baseline behavior and can skew perf comparisons. Prefer using the same default value (or defer to harness default when not provided) and keep help text aligned.

Proposed alignment patch
-debug-large-scale-3d n="10000" repair_every="2":
+debug-large-scale-3d n="10000" repair_every="4":
@@
-    `@echo` "  just debug-large-scale-3d [n] [repair_every] # Issue `#341`: 3D scalability (defaults n=10000, repair_every=2)"
+    `@echo` "  just debug-large-scale-3d [n] [repair_every] # Issue `#341`: 3D scalability (defaults n=10000, repair_every=4)"

Also applies to: 284-284

🤖 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 `@justfile` around lines 236 - 237, The recipe debug-large-scale-3d currently
defaults repair_every=2 which diverges from the large-scale harness default of
4; update the default to repair_every=4 (and likewise update the other
occurrence referenced) or make the parameter optional so the harness default is
used, and ensure any help text/comment near debug-large-scale-3d reflects the
new default to keep behavior and documentation aligned.
🤖 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.

Nitpick comments:
In `@justfile`:
- Around line 236-237: The recipe debug-large-scale-3d currently defaults
repair_every=2 which diverges from the large-scale harness default of 4; update
the default to repair_every=4 (and likewise update the other occurrence
referenced) or make the parameter optional so the harness default is used, and
ensure any help text/comment near debug-large-scale-3d reflects the new default
to keep behavior and documentation aligned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f49a2a05-d31a-43ce-8847-41a30137a92b

📥 Commits

Reviewing files that changed from the base of the PR and between 8726ad5 and 01d08d3.

📒 Files selected for processing (3)
  • justfile
  • src/triangulation/delaunay.rs
  • tests/large_scale_debug.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/large_scale_debug.rs

- Use the flip-based Delaunay verifier when accepting shuffled construction
  retries instead of the brute-force property scan.
- Add construction phase telemetry for preprocessing, insertion, finalization,
  and final validation timing.
- Default the large-scale debug repair interval to 2 for the 3K repair sweep.
Copy link
Copy Markdown
Contributor

@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/large_scale_debug.rs (1)

1151-1172: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Incremental mode never populates the new telemetry sections.

summary.telemetry and summary.slow_insertions are only filled in the batch ConstructionStatistics -> InsertionSummary path. In this manual insertion loop we only update attempt/skip counters, so the new telemetry output stays empty for ConstructionMode::Incremental, which is the mode this harness uses for step-by-step debugging/profiling.

🤖 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/large_scale_debug.rs` around lines 1151 - 1172, The incremental
insertion loop doesn't populate summary.telemetry or summary.slow_insertions
because only attempt/skip counters are updated; after each
insert_with_statistics match arm (both InsertionOutcome::Inserted and ::Skipped)
capture the returned stats and call the same telemetry/slow-insertion
aggregation used by the batch path—e.g. invoke the summary
telemetry/slow-insertion aggregation routines (the same logic used when
converting ConstructionStatistics -> InsertionSummary) inside the Ok((...,
stats)) arms so InsertionSummary (summary.telemetry and summary.slow_insertions)
is updated for ConstructionMode::Incremental as well.
🤖 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 `@tests/large_scale_debug.rs`:
- Around line 1197-1199: The validation cadence is currently advanced using the
loop index (idx + 1), which moves the schedule for duplicates/degenerate
vertices; change the call to validation_cadence.should_validate to use the count
of successful insertions (e.g., successful_inserts + 1) instead of idx + 1 so
validation is driven by actual mutations, and ensure successful_inserts is only
incremented when a vertex was truly inserted before calling
dt.as_triangulation().is_valid(); update any surrounding logic that assumes
idx-based advancement accordingly.

---

Outside diff comments:
In `@tests/large_scale_debug.rs`:
- Around line 1151-1172: The incremental insertion loop doesn't populate
summary.telemetry or summary.slow_insertions because only attempt/skip counters
are updated; after each insert_with_statistics match arm (both
InsertionOutcome::Inserted and ::Skipped) capture the returned stats and call
the same telemetry/slow-insertion aggregation used by the batch path—e.g. invoke
the summary telemetry/slow-insertion aggregation routines (the same logic used
when converting ConstructionStatistics -> InsertionSummary) inside the Ok((...,
stats)) arms so InsertionSummary (summary.telemetry and summary.slow_insertions)
is updated for ConstructionMode::Incremental as well.
🪄 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: 098761e4-6c85-4dd0-b091-cdbe5a260011

📥 Commits

Reviewing files that changed from the base of the PR and between 8726ad5 and a860437.

📒 Files selected for processing (4)
  • docs/dev/debug_env_vars.md
  • justfile
  • src/triangulation/delaunay.rs
  • tests/large_scale_debug.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/dev/debug_env_vars.md

Comment thread tests/large_scale_debug.rs Outdated
- Default batch construction repair to EveryN(2) while keeping direct incremental repair at EveryInsertion.
- Preserve final repair and validation as the acceptance gate for valid Delaunay output.
- Surface construction skip and slow-insertion diagnostics through the construction prelude.
- Align repair-policy docs with the batch default and the current 500/3000-point proxy results.
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Comment thread src/triangulation/locality.rs Fixed
Copy link
Copy Markdown
Contributor

@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

🤖 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/triangulation/locality.rs`:
- Line 19: This module is declared as pub(crate) so using pub(crate) on items
inside it is redundant; change the visibility of all module-scoped exported
items (e.g., the struct LocalConflictSeedCells and the other pub(crate)
types/functions found in this file) from pub(crate) to pub(super) so they are
exposed only to the parent module as intended; locate each occurrence of
"pub(crate)" in this file and replace it with "pub(super)".
🪄 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: 5ab6e1b4-e314-48bf-ac4f-54e129b8cc1c

📥 Commits

Reviewing files that changed from the base of the PR and between a860437 and c3f1c73.

📒 Files selected for processing (9)
  • docs/numerical_robustness_guide.md
  • docs/workflows.md
  • examples/pachner_roundtrip_4d.rs
  • src/core/triangulation.rs
  • src/lib.rs
  • src/triangulation/delaunay.rs
  • src/triangulation/locality.rs
  • tests/large_scale_debug.rs
  • tests/prelude_exports.rs
✅ Files skipped from review due to trivial changes (2)
  • examples/pachner_roundtrip_4d.rs
  • docs/numerical_robustness_guide.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/prelude_exports.rs
  • tests/large_scale_debug.rs
  • src/lib.rs
  • src/core/triangulation.rs

Comment thread src/triangulation/locality.rs Outdated
- Compare quality metrics only for cells shared across independently
  transformed triangulations, rejecting cases with no comparable cells.
- Narrow locality repair helper visibility where the current module graph
  allows it while preserving core insertion repair access.
- Clarify that non-substantive PRs may be declined unless justified by
  cleanup or tooling needs.
@acgetchell
Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

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

Caution

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

⚠️ Outside diff range comments (3)
src/core/triangulation.rs (2)

4129-4168: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Separate full validation from link-only validation in telemetry.

validation_after_insertion_work() now distinguishes FullValidation from RequiredTopologyLinks, but both paths still feed the same topology_validation_calls/nanos counters. That makes the new harness numbers hard to interpret: a full Level-3 pass and a cheaper ridge/vertex-link-only pass look identical in telemetry.

Please either record separate metrics per InsertionValidationWork or keep the existing topology_validation_* fields exclusive to FullValidation.

Also applies to: 4257-4266, 4319-4328

🤖 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/triangulation.rs` around lines 4129 - 4168, The telemetry currently
conflates full and link-only validations; update validate_after_insertion so the
metrics reflect the InsertionValidationWork variant returned by
validation_after_insertion_work(suspicion): call
validation_after_insertion_work, log the trigger as now, then in the match arm
for InsertionValidationWork::FullValidation wrap the is_valid() call with the
existing topology_validation_* counters (or increment the existing counters) and
in the InsertionValidationWork::RequiredTopologyLinks arm record a separate pair
of metrics (e.g., topology_link_validation_calls and
topology_link_validation_nanos) or skip touching the existing
topology_validation_* counters if you prefer exclusivity; adjust any uses in
log_validation_trigger_if_enabled/is_valid/validate_required_topology_links if
they currently assume a single metric so telemetry distinguishes full vs
link-only runs.

5519-5535: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't count caller-provided conflict buffers as conflict-region discovery.

These branches increment conflict_region_calls / conflict_region_cells_* even though no conflict search ran; the cells were supplied by the caller. That will overstate the new conflict-region telemetry whenever higher layers precompute conflicts, and makes those metrics incomparable with the branches that actually call find_conflict_region() or collect_local_exterior_conflict_seed_cells().

A separate provided_conflict_cells_* metric, or limiting conflict_region_* to true discovery work, would keep the harness output honest.

Also applies to: 5579-5590

🤖 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/triangulation.rs` around lines 5519 - 5535, The telemetry call
record_conflict_region_telemetry should not be invoked for caller-provided
conflict buffers (the match arm handling LocateResult::InsideCell(...),
Some(cells) where cells came from the caller); remove or skip that call in these
arms and instead either increment a new
provided_conflict_cells/provided_conflict_calls metric or only call
record_conflict_region_telemetry from the code paths that actually perform
discovery (the branches that call find_conflict_region() or
collect_local_exterior_conflict_seed_cells()); update both occurrences (the
InsideCell Some(cells) arm and the analogous branch around lines 5579–5590) to
use ensure_non_empty_conflict_cells for fallback behavior but not count them as
discovered conflict-region telemetry.
src/core/algorithms/locate.rs (1)

1-1: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add file-level unsafe prohibition to match repository Rust safety policy.

This changed Rust file should explicitly forbid unsafe code.

Proposed patch
 //! Point location algorithms for triangulations.
+#![forbid(unsafe_code)]
 //!
 //! Implements facet-walking point location for finding the cell containing

As per coding guidelines: "**/*.rs: Use #![forbid(unsafe_code)] - unsafe code is strictly forbidden in Rust code".

🤖 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/locate.rs` at line 1, Add the crate-level forbid by
inserting the attribute to prohibit unsafe code at the top of this Rust file:
add #![forbid(unsafe_code)] as the very first item in
src/core/algorithms/locate.rs so the module-level policy is enforced; ensure it
appears before any module docs or use/statements so the compiler applies the ban
to all functions/types in this file (e.g., the point-location functions and any
helper structs/enums defined here).
♻️ Duplicate comments (1)
tests/large_scale_debug.rs (1)

1198-1200: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validation cadence still advances on attempted vertices, not successful insertions.

The cadence call still passes idx + 1 here, so duplicates/degeneracies advance the validation schedule even though no mutation occurred. This drifts validation relative to repair (which is keyed off successful inserts) and can re-run is_valid() against an unchanged state. Drive cadence from summary.inserted and gate validation on a vertex actually being inserted this iteration.

🔧 Proposed fix
+                let mut inserted_this_round = false;
                 match dt.insert_with_statistics(vertex) {
                     Ok((InsertionOutcome::Inserted { .. }, stats)) => {
                         summary.record_inserted(stats);
+                        inserted_this_round = true;
                     }
                     Ok((InsertionOutcome::Skipped { error }, stats)) => {
@@
-                if had_cells
-                    && validation_cadence.should_validate(idx + 1)
+                if had_cells
+                    && inserted_this_round
+                    && validation_cadence.should_validate(summary.inserted)
                     && let Err(e) = dt.as_triangulation().is_valid()
                 {
🤖 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/large_scale_debug.rs` around lines 1198 - 1200, The validation cadence
is advanced using idx + 1 even when no vertex was actually inserted, causing
validation to drift; change the gating so that you only call
validation_cadence.should_validate(...) when this iteration inserted a vertex
(use summary.inserted as the counter rather than idx + 1) and only invoke
dt.as_triangulation().is_valid() when summary.inserted indicates a successful
insertion this loop; update the condition around
had_cells/validation_cadence.should_validate(...) to check summary.inserted (and
that the current vertex was inserted) before running is_valid().
🧹 Nitpick comments (1)
tests/proptest_triangulation.rs (1)

104-105: ⚡ Quick win

Tighten the proptest acceptance condition to avoid near-vacuous passes.

At Line 147, prop_assume!(matched_cells > 0) allows cases where only one cell is compared, which can under-test the transformation invariants.

Proposed adjustment
-    prop_assume!(matched_cells > 0);
+    let total_cells = tds_orig.number_of_cells().max(1);
+    // Keep degeneracy-tolerant behavior, but require meaningful overlap.
+    prop_assume!(matched_cells.saturating_mul(2) >= total_cells);
As per coding guidelines: “Proptests under `tests/proptest_*.rs` must cover algebraic and topological invariants … not just 'does it not panic'.”

Also applies to: 140-141, 147-147

🤖 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_triangulation.rs` around lines 104 - 105, The current proptest
acceptance uses prop_assume!(matched_cells > 0) which allows near-vacuous runs;
change the assumption to require multiple matched comparisons (e.g.,
prop_assume!(matched_cells > 1) or prop_assume!(matched_cells >= 2)) and, if
available, also assert the total number of cells considered is >1 so the test
exercises non-trivial triangulation behavior; update the prop_assume! call that
references matched_cells accordingly to ensure the test only accepts cases with
at least two matched cells (and adjust any related counters or variable names
that drive the comparison loop).
🤖 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 `@benches/ci_performance_suite.rs`:
- Around line 39-41: Add the crate-level attribute to forbid unsafe code by
inserting #![forbid(unsafe_code)] at the very top of the Rust source (before any
items or uses) in this benchmark file; ensure the attribute is the first
non-comment token so it applies to the crate, and run cargo check to confirm no
build issues from the added attribute.

In `@benches/topology_guarantee_construction.rs`:
- Around line 17-19: Add the crate-level attribute to forbid unsafe code by
inserting #![forbid(unsafe_code)] at the very top of the bench file (above the
use/imports) so the file complies with the policy; this affects the module that
imports DelaunayTriangulation, TopologyGuarantee, DelaunayRepairPolicy and
ValidationPolicy and must appear before those use statements.

In `@examples/delaunayize_repair.rs`:
- Around line 21-24: Add the crate-level prohibition by inserting
#![forbid(unsafe_code)] as the very first line of the Rust example file that
contains the Delaunay imports (the file that uses
DelaunayTriangulationConstructionError, DelaunayTriangulationValidationError and
the delaunay::prelude::* modules); this ensures the example conforms to the
Rust-file policy forbidding unsafe code.

In `@examples/numerical_robustness.rs`:
- Around line 12-15: This example file is missing the crate-level attribute that
forbids unsafe Rust; add the declaration `#![forbid(unsafe_code)]` at the very
top of the file (before the `use` statements and any other items) so the
compiler enforces no unsafe code in this example (affects this example
containing DelaunayTriangulation, DelaunayTriangulationConstructionError and
DelaunayTriangulationValidationError).

In `@tests/check_perturbation_stats.rs`:
- Around line 7-11: Add the crate-level attribute forbidding unsafe code to the
top of the file by inserting `#![forbid(unsafe_code)]` as the very first line in
tests/check_perturbation_stats.rs (before the existing `use` statements that
import DelaunayRepairPolicy, DelaunayTriangulation, TopologyGuarantee,
InsertionOutcome, and ValidationPolicy) so the file complies with the
project-wide Rust safety policy.

In `@tests/delaunay_incremental_insertion.rs`:
- Around line 15-17: Add a crate-level ban on unsafe code by inserting
#![forbid(unsafe_code)] at the very top of this test file (before the existing
use declarations that import
delaunay::prelude::triangulation::construction::{ConstructionOptions,
DedupPolicy, DelaunayTriangulation, TopologyGuarantee, vertex, ...}). This will
satisfy the repository policy that every Rust source must explicitly forbid
unsafe code.

In `@tests/euler_characteristic.rs`:
- Around line 17-20: Add the crate-level forbid for unsafe code at the very top
of this test file (before any use/imports) to enforce the project policy; modify
tests/euler_characteristic.rs so that the crate attribute forbidding unsafe code
is present above the existing use
delaunay::prelude::triangulation::construction::{...} import line and ensure it
applies to the whole crate.

---

Outside diff comments:
In `@src/core/algorithms/locate.rs`:
- Line 1: Add the crate-level forbid by inserting the attribute to prohibit
unsafe code at the top of this Rust file: add #![forbid(unsafe_code)] as the
very first item in src/core/algorithms/locate.rs so the module-level policy is
enforced; ensure it appears before any module docs or use/statements so the
compiler applies the ban to all functions/types in this file (e.g., the
point-location functions and any helper structs/enums defined here).

In `@src/core/triangulation.rs`:
- Around line 4129-4168: The telemetry currently conflates full and link-only
validations; update validate_after_insertion so the metrics reflect the
InsertionValidationWork variant returned by
validation_after_insertion_work(suspicion): call
validation_after_insertion_work, log the trigger as now, then in the match arm
for InsertionValidationWork::FullValidation wrap the is_valid() call with the
existing topology_validation_* counters (or increment the existing counters) and
in the InsertionValidationWork::RequiredTopologyLinks arm record a separate pair
of metrics (e.g., topology_link_validation_calls and
topology_link_validation_nanos) or skip touching the existing
topology_validation_* counters if you prefer exclusivity; adjust any uses in
log_validation_trigger_if_enabled/is_valid/validate_required_topology_links if
they currently assume a single metric so telemetry distinguishes full vs
link-only runs.
- Around line 5519-5535: The telemetry call record_conflict_region_telemetry
should not be invoked for caller-provided conflict buffers (the match arm
handling LocateResult::InsideCell(...), Some(cells) where cells came from the
caller); remove or skip that call in these arms and instead either increment a
new provided_conflict_cells/provided_conflict_calls metric or only call
record_conflict_region_telemetry from the code paths that actually perform
discovery (the branches that call find_conflict_region() or
collect_local_exterior_conflict_seed_cells()); update both occurrences (the
InsideCell Some(cells) arm and the analogous branch around lines 5579–5590) to
use ensure_non_empty_conflict_cells for fallback behavior but not count them as
discovered conflict-region telemetry.

---

Duplicate comments:
In `@tests/large_scale_debug.rs`:
- Around line 1198-1200: The validation cadence is advanced using idx + 1 even
when no vertex was actually inserted, causing validation to drift; change the
gating so that you only call validation_cadence.should_validate(...) when this
iteration inserted a vertex (use summary.inserted as the counter rather than idx
+ 1) and only invoke dt.as_triangulation().is_valid() when summary.inserted
indicates a successful insertion this loop; update the condition around
had_cells/validation_cadence.should_validate(...) to check summary.inserted (and
that the current vertex was inserted) before running is_valid().

---

Nitpick comments:
In `@tests/proptest_triangulation.rs`:
- Around line 104-105: The current proptest acceptance uses
prop_assume!(matched_cells > 0) which allows near-vacuous runs; change the
assumption to require multiple matched comparisons (e.g.,
prop_assume!(matched_cells > 1) or prop_assume!(matched_cells >= 2)) and, if
available, also assert the total number of cells considered is >1 so the test
exercises non-trivial triangulation behavior; update the prop_assume! call that
references matched_cells accordingly to ensure the test only accepts cases with
at least two matched cells (and adjust any related counters or variable names
that drive the comparison loop).
🪄 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: 368d087e-e37e-4097-9c47-b0ccc952767d

📥 Commits

Reviewing files that changed from the base of the PR and between aa0de2c and e5c3d5c.

📒 Files selected for processing (51)
  • CONTRIBUTING.md
  • README.md
  • benches/ci_performance_suite.rs
  • benches/large_scale_performance.rs
  • benches/profiling_suite.rs
  • benches/topology_guarantee_construction.rs
  • docs/api_design.md
  • docs/code_organization.md
  • docs/dev/debug_env_vars.md
  • docs/dev/rust.md
  • docs/diagnostics.md
  • docs/numerical_robustness_guide.md
  • docs/topology.md
  • docs/validation.md
  • docs/workflows.md
  • examples/delaunayize_repair.rs
  • examples/diagnostics.rs
  • examples/numerical_robustness.rs
  • examples/pachner_roundtrip_4d.rs
  • examples/topology_editing_2d_3d.rs
  • examples/zero_allocation_iterator_demo.rs
  • justfile
  • src/core/algorithms/locate.rs
  • src/core/operations.rs
  • src/core/triangulation.rs
  • src/lib.rs
  • src/triangulation.rs
  • src/triangulation/builder.rs
  • src/triangulation/delaunay.rs
  • src/triangulation/locality.rs
  • src/triangulation/validation.rs
  • tests/allocation_api.rs
  • tests/check_perturbation_stats.rs
  • tests/circumsphere_debug_tools.rs
  • tests/conflict_region_verification.rs
  • tests/dedup_batch_construction.rs
  • tests/delaunay_edge_cases.rs
  • tests/delaunay_incremental_insertion.rs
  • tests/delaunay_public_api_coverage.rs
  • tests/delaunay_repair_fallback.rs
  • tests/euler_characteristic.rs
  • tests/insert_with_statistics.rs
  • tests/large_scale_debug.rs
  • tests/prelude_exports.rs
  • tests/proptest_delaunay_triangulation.rs
  • tests/proptest_euler_characteristic.rs
  • tests/proptest_flips.rs
  • tests/proptest_orientation.rs
  • tests/proptest_triangulation.rs
  • tests/regressions.rs
  • tests/serialization_vertex_preservation.rs

Comment thread benches/ci_performance_suite.rs
Comment thread benches/topology_guarantee_construction.rs
Comment thread examples/delaunayize_repair.rs
Comment thread examples/numerical_robustness.rs
Comment thread tests/check_perturbation_stats.rs
Comment thread tests/delaunay_incremental_insertion.rs
Comment thread tests/euler_characteristic.rs
- Count only full insertion validation runs in topology validation telemetry while preserving required link checks.
- Exclude caller-provided conflict buffers from discovered conflict-region telemetry.
- Keep large-scale debug validation cadence tied to inserted vertices and tighten triangulation proptest acceptance.
- Add explicit unsafe-code forbids to remaining bench, example, and test crates.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api documentation Improvements or additions to documentation 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.

2 participants