Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
30 changes: 30 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ on:
branches: [main]
paths:
- "docs/**"
# The reference pages are generated from each crate's manifest, README
# and CHANGELOG.md, and rustdoc from its doc comments, so a crate-only
# change still alters the published site.
- "crates/**"
- "tools/xtask/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/docs.yml"
workflow_dispatch:

Expand All @@ -22,15 +29,38 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
with:
toolchain: 1.88.0
- name: Cache Rust build
# Dependencies only (rust-cache drops workspace artifacts), saved from
# main, which is the only ref this workflow runs on.
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
key: docs
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 20
cache: npm
cache-dependency-path: docs/package-lock.json
- name: Generated docs must match the crates they are derived from
run: cargo xtask docs --check
- run: npm ci
working-directory: docs
- run: npm run docs:build
working-directory: docs
# rustdoc is nested under the VitePress output so it ships in the one
# Pages artifact; the crate reference pages link to it at
# /kernel/api/rustdoc/<crate>/. The gate builds the same docs with
# -D warnings, so a broken intra-doc link fails before it lands.
- name: Build the Rust API reference
env:
RUSTDOCFLAGS: "-D warnings"
run: cargo doc --workspace --all-features --no-deps
- name: Nest rustdoc under the site at /api/rustdoc/
run: |
mkdir -p docs/.vitepress/dist/api/rustdoc
cp -r target/doc/. docs/.vitepress/dist/api/rustdoc/
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
Expand Down
6 changes: 0 additions & 6 deletions .github/workflows/roadmap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,17 @@ name: Roadmap freshness

# The roadmap explains ordering and reasoning; the project board carries status.
# This gate stops the roadmap drifting back into a status page that goes stale.
# It also covers crate `PLAN.md` design notes, which drifted the same way
# (kernel#25), so their paths must trigger it too.

on:
push:
branches: [main]
paths:
- "docs/ROADMAP.md"
- "crates/PLAN.md"
- "crates/**/PLAN.md"
- "scripts/check-roadmap-freshness.py"
- ".github/workflows/roadmap.yml"
pull_request:
paths:
- "docs/ROADMAP.md"
- "crates/PLAN.md"
- "crates/**/PLAN.md"
- "scripts/check-roadmap-freshness.py"
- ".github/workflows/roadmap.yml"
# Milestones change outside of any commit, so re-check on a schedule too.
Expand Down
127 changes: 89 additions & 38 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,104 @@
# Axiolid contribution rules
# axiolid/kernel

Axiolid is a standalone pure-Rust, IFC-agnostic geometry kernel. No source-format or vendor types may enter `crates/`. The `axiolid-model` DAG is the input seam; applications select operation providers.

Read the closest nested `AGENTS.md` for directory-specific rules. Preserve the pure-Rust, format-agnostic boundary: no IFC, file-format, or vendor model types in `crates/`. Run the focused crate checks while iterating and `scripts/gate.sh` before landing workspace-wide changes.
A standalone, pure-Rust, format-agnostic geometry kernel: neutral geometry
values, explicit operation contracts, and replaceable execution providers.
The `axiolid-model` DAG is the input seam, and applications select providers.
This file is the only `AGENTS.md`. Before editing a crate, read its
`README.md` and the `//!` docs of the module you touch (ADR 0078).

## Layout

- `crates/`: nested ownership tree containing publishable kernel packages; `crates/facade/axiolid` is the opt-in Rust facade and `crates/facade/axiolid-capi` is the sole unsafe C ABI boundary.
- `tools/xtask/`: local-only architecture checker and generated-document owner.
- `tools/benchmark/`: local-only measurement area — Criterion microbenchmarks, iai-callgrind instruction-count regression benchmarks, end-to-end scenarios, scaling assertions, and small regression datasets.
- `native/`: source-build and installed-package CMake integration; immutable source pins only.
- `docs/adr/`: durable architecture decisions; ADR 0035 owns the current package topology.
- `docs/architecture/`: current and generated crate/dependency maps.
- `docs/research/`: prior-art evidence.
- `architecture/capability-ledger.toml`: every geometry capability OCCT and CGAL have, graded against Axiolid, with reference paths and tracking issues. **Start here when choosing capability work:** `cargo xtask gaps` prints what is ready, by priority; `cargo xtask gaps show <row | issue-key | #number>` gives the evidence and the OCCT/CGAL packages to read. You do not need either library checked out.
- `scripts/`: feature, release, conformance, and mutation-verified architecture gates.
- `crates/`: publishable packages, nested by ownership: `foundation/`
(`axiolid-core`, the dependency root), `representations/`, `contracts/`,
`algorithms/`, `providers/`, `execution/`, `facade/` (`axiolid`, and
`axiolid-capi`, the only unsafe C ABI boundary).
- `tools/`: `xtask` (architecture, closure, context, docs, FFI and
ledger checks), `benchmark`, `oracle`. Local-only, never published.
- `tests/`: black-box consumer fixtures and downstream/native probes.
- `native/`: CMake source-build and installed-package integration.
- `docs/`: the VitePress site. ADRs live in `docs/adr/`.
`docs/architecture/` holds the machine-checked declarations:
`closure-profiles.toml` lists minimal downstream closures,
`capability-ledger.toml` grades every OCCT/CGAL capability against
Axiolid, and `semver-exceptions.toml` lists accepted breaking changes.
Its generated pages (the architecture maps, `docs/reference/`) come
from `cargo xtask docs` and are never edited by hand: change the
crate's manifest, `README.md` or `CHANGELOG.md` and regenerate.

## Commands
## Dependency rule

```bash
cargo fmt --all -- --check
cargo build --workspace
cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo xtask architecture check
bash scripts/probe_layering_gate.sh
bash scripts/field_gate.sh
bash scripts/geometry-feature-matrix.sh
scripts/check-capi.sh
scripts/check-native-packaging.sh
scripts/gate.sh
```
Production direction is
`foundation <- representations <- contracts <- algorithms/providers <- execution <- facade`.
This is a role DAG, not a licence to depend on every earlier layer: each
crate's exact internal edges are allowlisted in its
`[package.metadata.axiolid]`, and `cargo xtask architecture check` enforces
them together with naming (ADR 0064), placement, unsafe policy and
format neutrality. Contracts never depend on providers or dispatch.
Algorithms do not select execution policy. Upward edges are dev-only and
limited to conformance tests. A closure change is an API change: update
`expected_internal` deliberately and record why in an ADR, never to
silence the gate. ADR 0035 owns the package topology.

## Behaviour rules

- No IFC, file-format, vendor, renderer or GPU-API types in `crates/`.
No C++ dependency path.
- `unsafe` is forbidden everywhere except `axiolid-capi`, which denies
unsafe operations inside unsafe functions.
- Refuse with a typed error or diagnostic. Never return substitute
geometry, a guessed default or a silently degraded result. Broad-phase
candidates are never labelled exact.
- A provider advertises only what it implements. It lands after its typed
contract, refusal behaviour and conformance suite, and it needs a portable
scalar correctness oracle before claiming an operation trait. Concrete
providers stay optional. Never tessellate exact intent silently.
- A capability claim needs an implementation, typed refusal and
conformance evidence; package metadata is not one. A performance claim
needs a committed benchmark confirmed in wall clock, not only in
instruction counts.
- Output order is deterministic. CPU dispatch is chosen at runtime, never
by `target-cpu=native`.
- Public values implement `Debug` and `Clone`; add other standard traits
only when they are semantically valid. Split a module before unrelated
data, validation and algorithms grow together; add no placeholder files.

`scripts/gate.sh` needs `cargo-semver-checks` for the breaking-change gate.
Install it once; pin the version because newer releases require a newer
rustc than this workspace uses:
## Open work

Open work lives in GitHub issues (ADR 0078). A marker in code names its
issue as `TODO(#N)`. Do not add plans, checklists, progress logs or
nested `AGENTS.md` files: `cargo xtask context check` rejects them.
To choose capability work, start from the ledger: `cargo xtask gaps` lists
what is ready by priority, and `cargo xtask gaps show <row|issue>` gives the
evidence and the OCCT/CGAL packages to read. A landing that changes a
row's level edits that row in the same commit. `scoped` rows and
`needs_decision` issues are maintainer decisions: ask, don't start coding.

## Gate

Iterate with focused crate checks, then run the full gate before landing
workspace-wide changes. Judge it by exit code:

```bash
cargo install cargo-semver-checks --version 0.44.0 --locked
scripts/gate.sh
```

The gate fails loudly if it is missing rather than skipping the check.

Benchmarks are not part of `scripts/gate.sh`; run them explicitly:
It needs `cargo-semver-checks`, pinned because newer releases need a
newer rustc: `cargo install cargo-semver-checks --version 0.44.0 --locked`.
The main steps can run alone:

```bash
cargo bench -p axiolid-benchmark --bench micro # wall-clock, informative
cargo bench -p axiolid-benchmark --bench scenario # end-to-end via the facade
bash scripts/bench-regression.sh # instruction counts, CI gate
cargo xtask architecture check # after metadata changes: cargo xtask architecture docs
cargo xtask architecture closure check
cargo xtask context check
cargo xtask docs --check # after README, CHANGELOG or metadata changes: cargo xtask docs
cargo xtask gaps check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
scripts/geometry-feature-matrix.sh
scripts/check-capi.sh
```

The workspace has no C++ dependency path. `axiolid-capi` is the sole audited unsafe Rust boundary and must deny unsafe operations in unsafe functions; every other facade, contract, representation, and foundation crate forbids unsafe code. Concrete execution providers must remain optional and require a portable scalar correctness oracle before claiming an operation trait.
Mutation-verify a new gate before trusting it: break the rule, watch it
fail, restore (`scripts/probe_*_gate.sh`). Benchmarks are not in the gate;
`tools/benchmark/README.md` says how to run them. Records (changelog,
ADRs, research) follow `docs/guide/contributing.md`.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ readme = "README.md"

[workspace.dependencies]
# Format-agnostic Axiolid geometry kernel. Leaf crates remain independently consumable.
axiolid = { path = "crates/facade/axiolid", version = "0.3.0", default-features = false }
axiolid = { path = "crates/facade/axiolid", version = "0.4.0", default-features = false }
axiolid-core = { path = "crates/foundation/core", version = "0.3.0" }
axiolid-mesh = { path = "crates/representations/discrete/mesh", version = "0.3.0" }
axiolid-pointcloud = { path = "crates/representations/discrete/pointcloud", version = "0.3.0" }
Expand Down Expand Up @@ -117,7 +117,7 @@ num-integer = { version = "0.1.46", default-features = false, features = ["std"]
axiolid-predicates = { path = "crates/algorithms/predicates", version = "0.3.1", default-features = false }
axiolid-exact = { path = "crates/algorithms/exact", version = "0.1.1" }
axiolid-linear-intersection = { path = "crates/algorithms/query/intersection/linear", version = "0.3.0", default-features = false }
axiolid-ray-mesh = { path = "crates/algorithms/query/intersection/ray-mesh", version = "0.3.0", default-features = false }
axiolid-ray-mesh = { path = "crates/algorithms/query/intersection/ray-mesh", version = "0.4.0", default-features = false }
axiolid-reference = { path = "crates/algorithms/reference", version = "0.3.0" }
axiolid-mesh-compile = { path = "crates/execution/compile", version = "0.3.4" }
# axiolid-oracle is publish = false (test-only oracle crate, tools/oracle) and
Expand Down
126 changes: 0 additions & 126 deletions GAP2-FINDING.md

This file was deleted.

Loading
Loading