# Code Organization Guide

This document lists the files checked into the repository and summarizes the architecture-sensitive module boundaries for the causal-triangulations crate.

## Project Structure

### Complete Directory Tree

> **Tip**: regenerate this tree from tracked files:
>
> ```bash
> git --no-pager ls-files | LC_ALL=C sort | \
>   LC_ALL=C tree -a --charset UTF-8 --dirsfirst --noreport -F --fromfile
> ```
>
> This keeps the directory tree synchronized with the files that GitHub will display.

```text
causal-triangulations/
├── .github/
│   ├── workflows/
│   │   ├── audit.yml
│   │   ├── ci.yml
│   │   ├── codecov.yml
│   │   ├── codeql.yml
│   │   ├── performance.yml
│   │   ├── rust-clippy.yml
│   │   ├── semgrep-sarif.yml
│   │   └── zizmor.yml
│   ├── CODEOWNERS
│   └── dependabot.yml
├── benches/
│   ├── README.md
│   ├── cdt_benchmarks.rs
│   └── ci_performance_suite.rs
├── docs/
│   ├── dev/
│   │   ├── commands.md
│   │   ├── python.md
│   │   ├── rust.md
│   │   ├── testing.md
│   │   └── tooling-alignment.md
│   ├── CLI_EXAMPLES.md
│   ├── PERFORMANCE_TESTING.md
│   ├── RELEASING.md
│   ├── code_organization.md
│   ├── foliation.md
│   ├── metropolis.md
│   ├── moves.md
│   ├── roadmap.md
│   └── testing.md
├── examples/
│   ├── scripts/
│   │   ├── README.md
│   │   ├── basic_simulation.sh
│   │   ├── parameter_sweep.sh
│   │   └── performance_test.sh
│   ├── basic_cdt.rs
│   ├── find_good_seeds.rs
│   ├── observables.rs
│   └── output_and_checkpoint.rs
├── proptest-regressions/
│   └── cdt/
│       └── triangulation.txt
├── scripts/
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── test_archive_changelog.py
│   │   ├── test_benchmark_models.py
│   │   ├── test_benchmark_utils.py
│   │   ├── test_coverage_report.py
│   │   ├── test_hardware_utils.py
│   │   ├── test_postprocess_changelog.py
│   │   ├── test_subprocess_utils.py
│   │   └── test_tag_release.py
│   ├── README.md
│   ├── archive_changelog.py
│   ├── benchmark_models.py
│   ├── benchmark_utils.py
│   ├── check_semgrep_fixtures.py
│   ├── coverage_report.py
│   ├── hardware_utils.py
│   ├── performance_analysis.py
│   ├── postprocess_changelog.py
│   ├── run_all_examples.sh
│   ├── subprocess_utils.py
│   └── tag_release.py
├── src/
│   ├── cdt/
│   │   ├── action.rs
│   │   ├── ergodic_moves.rs
│   │   ├── foliation.rs
│   │   ├── metropolis/
│   │   │   ├── adapter.rs
│   │   │   ├── checkpoint.rs
│   │   │   ├── helpers.rs
│   │   │   ├── runner.rs
│   │   │   └── telemetry.rs
│   │   ├── observables.rs
│   │   ├── results.rs
│   │   └── triangulation/
│   │       ├── builders.rs
│   │       ├── foliation.rs
│   │       ├── moves.rs
│   │       ├── state.rs
│   │       └── validation.rs
│   ├── geometry/
│   │   ├── backends/
│   │   │   ├── delaunay.rs
│   │   │   └── mock.rs
│   │   ├── generators.rs
│   │   ├── operations.rs
│   │   └── traits.rs
│   ├── config.rs
│   ├── errors.rs
│   ├── lib.rs
│   ├── main.rs
│   └── util.rs
├── tests/
│   ├── semgrep/
│   │   ├── .github/
│   │   │   └── workflows/
│   │   │       └── action_policy.yml
│   │   ├── docs/
│   │   │   └── command_order.sh
│   │   ├── doctests/
│   │   │   └── unwrap_expect.txt
│   │   ├── scripts/
│   │   │   └── tests/
│   │   │       └── python_exceptions.py
│   │   └── src/
│   │       └── project_rules/
│   │           ├── bench_example_usage.rs
│   │           └── rust_style.rs
│   ├── cli.rs
│   ├── integration_tests.rs
│   ├── large_scale_debug.rs
│   ├── physics_integration.rs
│   ├── proptest_foliation.rs
│   ├── proptest_metropolis.rs
│   └── regressions.rs
├── .bencher.toml
├── .codecov.yml
├── .coderabbit.yml
├── .gitignore
├── .python-version
├── .taplo.toml
├── .yamllint
├── AGENTS.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CITATION.cff
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── REFERENCES.md
├── SECURITY.md
├── cliff.toml
├── clippy.toml
├── dprint.json
├── justfile
├── pyproject.toml
├── rumdl.toml
├── rust-toolchain.toml
├── rustfmt.toml
├── semgrep.yaml
├── ty.toml
├── typos.toml
└── uv.lock
```

## Architecture Layers

The crate is split into two intentionally different layers:

- `src/geometry/` is the backend interface layer. It is the only layer that talks directly to the `delaunay` crate, wrapping upstream types behind crate-owned
  traits, opaque handles, generators, and backend adapters.
- `src/cdt/` is the CDT domain layer. It owns causal triangulation semantics: foliation, topology and causality checks, ergodic moves, Regge action, Metropolis
  sampling adapters, measurements, and observables.

Code outside `src/geometry/` must not import `delaunay::` directly. CDT modules should depend on `TriangulationQuery` / `TriangulationMut`, crate-owned Delaunay
handle wrappers, `DelaunayBackend2D`, and generator functions from `crate::geometry`.

Generic MCMC mechanics should be delegated to `markov-chain-monte-carlo` through thin CDT adapters. CDT owns domain state, proposal-site enumeration,
foliation/topology validation, measurements, and result translation; the upstream MCMC crate owns Metropolis-Hastings acceptance, proposal-ratio application,
chain counters, planned-proposal commit ordering, and reusable sampler continuation behavior. Repository-owned Semgrep rules enforce the issue #155 boundary
against new CDT-local generic acceptance draws or manual accepted/rejected sampler counters.

## Key Modules

### `cdt/foliation.rs` — Foliation

Assigns each vertex to a discrete time slice, enabling classification of edges as spacelike or timelike and triangles as up or down. See `docs/foliation.md` for
design details.

- `Foliation` — aggregate bookkeeping (per-slice vertex counts, total slices)
- `EdgeType` — `Spacelike` (same slice) or `Timelike` (adjacent slices)
- `SimplexType` — `Up` (2,1) or `Down` (1,2) triangle classification, encoded as `i32` simplex data
- Time labels are stored directly as vertex data (`Vertex.data: Option<u32>`), mirroring CDT-plusplus’s `vertex->info()`

### `cdt/triangulation/` — CDT Triangulation State and Builders

This is CDT domain logic layered over the geometry backend interface. It may use `DelaunayBackend2D` and crate-owned Delaunay handles, but it does not reach
through to upstream `delaunay::` APIs directly.

- Owns the `CdtTriangulation` state, `CdtMetadata`, `SimulationEvent`, metadata validation, cached simplex-count accessors, and common backend-agnostic state
  methods
- `CdtSimplexCounts` carries the CDT-domain proof that constructed triangulations have positive vertex, edge, and triangle counts as `NonZeroUsize`, while
  raw geometry queries remain `usize` so backend construction, clearing, and collection-style count APIs can represent zero
- `from_cdt_strip(vertices_per_slice, num_slices)` — Delaunay-built open-boundary 1+1 CDT strip with strict Up/Down simplex classification and upstream Level
  1–4 Delaunay validation before wrapping
- `from_cdt_strip_profile(volume_profile)` — open-boundary 1+1 CDT strip from explicit nonuniform per-slice vertex counts; builds labeled point data and
  delegates triangulation to the Delaunay constructor before strict initial validation
- `from_toroidal_cdt(vertices_per_slice, num_slices)` — periodic Delaunay S¹×S¹ toroidal CDT (χ = 0) with upstream Level 1–4 validation before wrapping;
  requires `vertices_per_slice ≥ 3` and `num_slices ≥ 3`
- `from_toroidal_cdt_profile(volume_profile)` — periodic S¹×S¹ toroidal CDT from explicit per-slice vertex counts, preserving closed spatial slices and
  periodic time
- `assign_foliation_by_y(num_slices)` — bin existing vertices into time slices
- Query methods: `time_label`, `edge_type`, `vertices_at_time`, `slice_sizes`, `has_foliation`
- Validation: constructors require upstream Delaunay Level 1–4 validation for initial meshes; `validate()` is the post-move/final-state contract and requires
  upstream structural validity plus CDT topology, foliation, causality, and strict Up/Down simplex classification
- Mutable backend access is not exposed. CDT code mutates Delaunay state only through narrow crate-internal operations (`flip_edge`, `subdivide_face`,
  `remove_vertex`, `set_vertex_data`) that invalidate cached counts and foliation synchronization bookkeeping on success.

The implementation lives under `src/cdt/triangulation/` and is wired from `src/lib.rs` to avoid `mod.rs` files:

- `builders.rs` — Delaunay-backed random/seeded/labeled builders plus strip and periodic toroidal CDT builders
- `foliation.rs` — foliation assignment, slice and label queries, volume profiles, simplex/edge classification, and foliation synchronization
- `moves.rs` — narrow crate-internal Delaunay mutation hooks used by ergodic moves
- `state.rs` — module entry point, `CdtTriangulation`, `CdtMetadata`, `SimulationEvent`, serialization, cached geometry accessors, and backend-agnostic
  state methods
- `validation.rs` — full CDT validation and Delaunay-backed causality checks

### `config.rs` — `CdtTopology` enum

- `OpenBoundary` (default) — finite strip with boundary, χ ∈ {1, 2}
- `Toroidal` — periodic in space and time, S¹×S¹, χ = 0
- Wired through `CdtConfig.topology`, `CdtConfigOverrides.topology`, the CLI `--topology` flag, and `CdtMetadata.topology`
- `run_simulation()` accepts `ValidatedCdtConfig` and dispatches on topology and profile mode: regular `Toroidal` → `from_toroidal_cdt`, regular
  `OpenBoundary` → `from_cdt_strip`, and explicit `CdtConfig.volume_profile` → the matching profile constructor; `vertices` is always the total vertex count

### `cdt/metropolis/` — Metropolis move ordering

`MetropolisAlgorithm::run()` is the current CDT production runner for proposal-before-mutation sampling. It proposes a move type, samples an explicit local
proposal site from the same universe used for proposal counts, plans that site on a cloned triangulation, computes `ΔS` and the forward/reverse
Metropolis-Hastings site-count ratio, accepts or rejects the concrete proposal, and only replaces the live triangulation after acceptance. Ordinary causal,
geometric, and backend edit failures are self-loop proposal outcomes recorded in `ProposalStatistics`; hard backend failures remain structured errors.
Toroidal move finalization rejects and rolls back candidate sites that would violate χ = 0 or the closed-S¹ per-slice foliation invariant.

The module tree is declared from `src/lib.rs` to avoid the `metropolis.rs` plus `metropolis/` layout pattern. `adapter.rs` is the single CDT adapter boundary
for `markov-chain-monte-carlo` proposal and target traits. `runner.rs` owns `MetropolisAlgorithm::run_steps`, which rebuilds the upstream `Chain`/`Sampler`
continuation view, delegates generic acceptance, proposal-ratio application, chain counters, and planned-proposal commit ordering to the upstream MCMC crate,
then records CDT-specific telemetry, measurements, and result state. `checkpoint.rs` owns resumable checkpoint state and resume validation, `telemetry.rs` owns
public step/proposal telemetry, and `helpers.rs` holds shared CDT-domain calculations.

See `docs/metropolis.md` for the current planned-proposal ordering and
[`causal-triangulations#155`](https://github.com/acgetchell/causal-triangulations/issues/155) for the remaining MCMC boundary follow-up.

### `cdt/results.rs` — Simulation outputs

- `Measurement` records per-step action, simplex counts, and optional per-slice volume profiles.
- `SimulationResultsBackend` owns the final triangulation, Monte Carlo step telemetry, upstream scalar trace rows, move statistics, and measurement history.
- Result methods summarize acceptance rate, average action, post-thermalization volume profiles, sample volume fluctuations, and final-state Hausdorff/spectral
  dimension estimates.
- `scalar_trace()` and `write_trace_csv()` expose step diagnostics through `markov-chain-monte-carlo::Trace`, using a rectangular CSV table suitable for Polars
  and other downstream dataframe tools.

### `cdt/observables.rs` — User-facing estimators

- `estimate_hausdorff_dimension` — estimates Hausdorff dimension from combinatorial dual-graph geodesic ball growth, returning `None` when the triangulation
  is too small or live face adjacency cannot be resolved
- `estimate_spectral_dimension` — estimates spectral dimension from dual-graph diffusion return probability, returning `None` when the graph is too small or
  lacks enough positive return-probability samples for a fit
- `CdtTriangulation::volume_profile` measures per-slice triangle counts on a triangulation; `SimulationResultsBackend` provides aggregate volume-profile
  summaries for simulation outputs
- Import triangulation-focused analysis APIs through `prelude::observables`; use `prelude::simulation` when constructing or inspecting simulation result
  containers

### `geometry/traits.rs` — Backend-neutral interface

- `GeometryBackend` defines associated coordinate, handle, and error types for a geometry implementation
- `TriangulationQuery` is the read-only surface used by CDT logic for counts, handles, adjacency, coordinates, face vertices, and validation
- `TriangulationMut` is the narrow mutation surface used by CDT-owned move kernels through CDT state mutation methods, not broad mutable backend exposure
- Result structs such as `FlipResult`, `EdgeAdjacentFaces`, and `SubdivisionResult` keep local topology operations backend-neutral
- Use `prelude::geometry` for real backend construction and geometry traits; use `prelude::testing` for mock-backend doctests or downstream fixture code

### `geometry/backends/delaunay.rs` — Delaunay adapter

- Wraps the upstream `delaunay` triangulation in `DelaunayBackend`
- Defines crate-owned opaque handles (`DelaunayVertexHandle`, `DelaunayEdgeHandle`, `DelaunayFaceHandle`) so CDT code does not depend on upstream key types
- Translates upstream Delaunay operations and errors into this crate's trait contracts
- Together with `geometry/generators.rs`, this is the only place that directly imports from the `delaunay` crate

### `geometry/generators.rs` — Delaunay triangulation generators

- `generate_delaunay2` — builds a 2D Delaunay triangulation with optional seed
- `build_delaunay2_with_data` — builds from coordinate + vertex-data pairs
- `build_delaunay2_from_simplices` / `build_delaunay2_with_topology` — builds from explicit simplex connectivity (no Delaunay point insertion); the latter
  also accepts `TopologyGuarantee` and `GlobalTopology` metadata for supported explicit topologies
- `build_toroidal_delaunay2` — API-compatibility wrapper for explicit toroidal meshes; with `delaunay` v0.7.8 it validates the domain and reports the upstream
  explicit-toroidal topology limitation
- `build_periodic_toroidal_delaunay2` — builds true periodic toroidal Delaunay meshes through the upstream image-point constructor
- `random_delaunay2`, `seeded_delaunay2` — convenience wrappers
- `DelaunayTriangulation2D` — type alias for the concrete 2D triangulation type

Together with `backends/delaunay.rs`, this module is the only place that directly imports from the `delaunay` crate.

The toroidal CDT constructor builds from labeled lattice vertices and delegates to the upstream periodic image-point constructor, then validates the resulting
Delaunay triangulation before CDT foliation, causality, topology, and simplex-classification checks run.

### `util.rs` — Numeric helpers

- `saturating_usize_to_i32` — crate-internal usize→i32 conversion for Euler characteristic arithmetic
- Simulation action inputs use native `usize` topology counts; validated CDT count snapshots use `NonZeroUsize`; measurement and trace telemetry downcasts to
  `u32` are checked at construction boundaries
- `y_to_time_bucket` — f64→Option<u32> via round(), for time-slice assignment
- `f64_band_to_u32` — f64→u32 clamped, for y-coordinate binning

## Key Dependencies

- `delaunay` (v0.7.8) — geometry backend (Delaunay triangulations, vertex data for time labels, checked TDS reconstruction with topology context,
  `set_vertex_data_by_key` for O(1) label mutation)
- `markov-chain-monte-carlo` (v0.4) — MCMC framework (`DelayedProposal`, `Chain::step_delayed`, `Target`)
- `num-traits` — `ToPrimitive` and `NumCast` for checked or saturating numeric conversions
