Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
22667b2
feat(geom): BroadPhase trait + AabbTree reference impl (split PR 3)\n…
flyingrobots Oct 27, 2025
b921b29
docs(geom): document O(n^2) baseline and planned deterministic SAP/BV…
flyingrobots Oct 27, 2025
a428726
Merge remote-tracking branch 'origin/main' into echo/split-broadphase
flyingrobots Oct 28, 2025
40d9ba7
geom(broadphase PR #9): merge main to register rmg-geom crate; enable…
flyingrobots Oct 28, 2025
00f8f0d
geom(PR #9): adopt physics-friendly naming — swept volume\n\n- tempor…
flyingrobots Oct 28, 2025
40ec39e
docs: add spec-timecube (3-axis time) and spec-knots-in-time (knot di…
flyingrobots Oct 28, 2025
4ebd6c3
core(snapshot): adopt header v1 (parents + digests); commit/snapshot …
flyingrobots Oct 29, 2025
76f2f31
toolchain: default 1.71.1; add MSRV CI job for rmg-core/geom on 1.68;…
flyingrobots Oct 29, 2025
b915fea
toolchain(floor): raise workspace MSRV to 1.71.1; bump rust-version a…
flyingrobots Oct 29, 2025
f47b3af
hooks: restrict missing_docs ban to library src files; allow tests/bu…
flyingrobots Oct 29, 2025
e0da662
geom(broad): remove redundant sort and unused import; specs/CI updated
flyingrobots Oct 29, 2025
56cbfc2
geom(broad): drop unused Ordering import after removing redundant sort
flyingrobots Oct 29, 2025
734499e
core(snapshot): unify decision_digest empty semantics across commit()…
flyingrobots Oct 29, 2025
e734dc5
fmt: apply rustfmt after hooks/constant changes
flyingrobots Oct 29, 2025
ea45703
hooks(pre-push): add MSRV var with global skip when crates require ne…
flyingrobots Oct 29, 2025
2689fdb
MSRV: set hook default to 1.71.1; execution plan reflects single 1.71…
flyingrobots Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail

echo "[devcontainer] Installing MSRV toolchain (1.68.0) and respecting rust-toolchain.toml..."
echo "[devcontainer] Installing default toolchain (1.71.1 via rust-toolchain.toml)..."
if ! command -v rustup >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail https://sh.rustup.rs | sh -s -- --default-toolchain none -y
export PATH="$HOME/.cargo/bin:$PATH"
fi

rustup toolchain install 1.68.0 --profile minimal
# Do not override default; let rust-toolchain.toml control toolchain selection for this repo.
# Install optional newer toolchain for local convenience (kept as non-default).
rustup toolchain install 1.90.0 --profile minimal || true
# Ensure components/targets are available for the active (rust-toolchain.toml) toolchain.
rustup component add rustfmt clippy || true
rustup target add wasm32-unknown-unknown || true
rustup toolchain install 1.71.1 --profile minimal
# Do not override default; let rust-toolchain.toml control selection for this repo.
# Ensure components/targets are available for the default toolchain (1.71.1).
rustup component add --toolchain 1.71.1 rustfmt clippy || true
rustup target add --toolchain 1.71.1 wasm32-unknown-unknown || true

echo "[devcontainer] Priming cargo registry cache (optional)..."
cargo fetch || true
Expand Down
17 changes: 12 additions & 5 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,19 @@ if [[ -n "$CORE_API_CHANGED" ]]; then
echo "$STAGED" | grep -Fx 'docs/decision-log.md' >/dev/null || { echo 'pre-commit: docs/decision-log.md must be updated when core API changes.' >&2; exit 1; }
fi

# 5) Lockfile guard: ensure lockfile version is v3 (compatible with MSRV cargo)
# 5) Lockfile guard: ensure lockfile version is v3 (current cargo format)
if [[ -f Cargo.lock ]]; then
VER_LINE=$(grep -n '^version = ' Cargo.lock | head -n1 | awk -F'= ' '{print $2}')
if [[ "$VER_LINE" != "3" && "$VER_LINE" != "3\r" ]]; then
echo "pre-commit: Cargo.lock must be generated with Cargo 1.68 (lockfile v3)." >&2
echo "Run: cargo +1.68.0 generate-lockfile" >&2
# Normalize detected lockfile version (strip quotes/CR/whitespace)
VER_LINE=$(grep -n '^version = ' Cargo.lock | head -n1 | awk -F'= ' '{print $2}' | tr -d '\r' | tr -d '"' | xargs)
if [[ "$VER_LINE" != "3" ]]; then
# Determine pinned toolchain (normalize), fallback to rust-toolchain.toml if unset
_PINNED_RAW="${PINNED:-}"
if [[ -z "$_PINNED_RAW" ]]; then
_PINNED_RAW=$(awk -F '"' '/^channel/ {print $2}' rust-toolchain.toml 2>/dev/null || echo "")
fi
PINNED_NORM=$(printf "%s" "$_PINNED_RAW" | tr -d '\r' | xargs)
echo "pre-commit: Cargo.lock must be lockfile format v3 (found '$VER_LINE')." >&2
echo "Run: cargo +${PINNED_NORM} generate-lockfile" >&2
exit 1
fi
fi
Expand Down
80 changes: 62 additions & 18 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
PINNED="${PINNED:-1.68.0}"
PINNED="${PINNED:-1.71.1}"
# MSRV floor for library checks (override with MSRV env)
MSRV="${MSRV:-1.71.1}"

for cmd in cargo rustup rg; do
if ! command -v "$cmd" >/dev/null 2>&1; then
Expand All @@ -15,34 +17,76 @@ if [[ "${SKIP_HOOKS:-}" == 1 ]]; then
exit 0
fi

echo "[pre-push] fmt"
cargo +"$PINNED" fmt --all -- --check
echo "[pre-push] fmt (default toolchain)"
cargo fmt --all -- --check

echo "[pre-push] clippy (workspace)"
cargo +"$PINNED" clippy --all-targets -- -D warnings -D missing_docs
echo "[pre-push] clippy (workspace, default toolchain)"
cargo clippy --all-targets -- -D warnings -D missing_docs

echo "[pre-push] tests (workspace)"
cargo +"$PINNED" test --workspace
echo "[pre-push] tests (workspace, default toolchain)"
cargo test --workspace

# MSRV check for rmg-core
echo "[pre-push] MSRV check (rmg-core @ $PINNED)"
if rustup run "$PINNED" cargo -V >/dev/null 2>&1; then
cargo +"$PINNED" check -p rmg-core --all-targets
echo "[pre-push] Testing against MSRV ${MSRV} (core libraries)"
# If any participating crate declares a rust-version greater than MSRV, skip MSRV checks entirely.
CORE_RV=$(awk -F '"' '/^rust-version/ {print $2}' crates/rmg-core/Cargo.toml 2>/dev/null || echo "")
GEOM_RV=$(awk -F '"' '/^rust-version/ {print $2}' crates/rmg-geom/Cargo.toml 2>/dev/null || echo "")
if { [[ -n "$CORE_RV" ]] && printf '%s\n%s\n' "$MSRV" "$CORE_RV" | sort -V | tail -n1 | grep -qx "$CORE_RV" && [[ "$CORE_RV" != "$MSRV" ]]; } \
|| { [[ -n "$GEOM_RV" ]] && printf '%s\n%s\n' "$MSRV" "$GEOM_RV" | sort -V | tail -n1 | grep -qx "$GEOM_RV" && [[ "$GEOM_RV" != "$MSRV" ]]; }; then
echo "[pre-push] Skipping MSRV block: one or more crates declare rust-version > ${MSRV} (core=${CORE_RV:-unset}, geom=${GEOM_RV:-unset})"
else
echo "[pre-push] MSRV toolchain $PINNED not installed. Install via: rustup toolchain install $PINNED" >&2
exit 1
if ! rustup run "$MSRV" cargo -V >/dev/null 2>&1; then
echo "[pre-push] MSRV toolchain ${MSRV} not installed. Install via: rustup toolchain install ${MSRV}" >&2
exit 1
fi
# Only run MSRV tests for crates that declare rust-version <= MSRV; skip otherwise.
msrv_ok() {
local crate="$1"
local rv
rv=$(awk -F '"' '/^rust-version/ {print $2}' "crates/${crate}/Cargo.toml" 2>/dev/null || echo "")
if [[ -z "$rv" ]]; then
return 0
fi
# If declared rust-version is greater than MSRV, skip.
if printf '%s\n%s\n' "$MSRV" "$rv" | sort -V | tail -n1 | grep -qx "$rv" && [[ "$rv" != "$MSRV" ]]; then
echo "[pre-push] Skipping MSRV test for ${crate} (rust-version ${rv} > MSRV ${MSRV})"
return 1
fi
# If crate depends on workspace rmg-core whose rust-version exceeds MSRV, skip as well
if grep -qE '^rmg-core\s*=\s*\{[^}]*path\s*=\s*"\.\./rmg-core"' "crates/${crate}/Cargo.toml" 2>/dev/null; then
local core_rv
core_rv=$(awk -F '"' '/^rust-version/ {print $2}' "crates/rmg-core/Cargo.toml" 2>/dev/null || echo "")
if [[ -n "$core_rv" ]] && printf '%s\n%s\n' "$MSRV" "$core_rv" | sort -V | tail -n1 | grep -qx "$core_rv" && [[ "$core_rv" != "$MSRV" ]]; then
echo "[pre-push] Skipping MSRV test for ${crate} (depends on rmg-core ${core_rv} > MSRV ${MSRV})"
return 1
fi
fi
return 0
}
if msrv_ok rmg-core; then cargo +"$MSRV" test -p rmg-core --all-targets; fi
if msrv_ok rmg-geom; then cargo +"$MSRV" test -p rmg-geom --all-targets; fi
fi

# Rustdoc warnings guard (core API)
echo "[pre-push] rustdoc warnings gate (rmg-core)"
# Rustdoc warnings guard (public crates)
echo "[pre-push] rustdoc warnings gate (rmg-core @ $PINNED)"
RUSTDOCFLAGS="-D warnings" cargo +"$PINNED" doc -p rmg-core --no-deps
echo "[pre-push] rustdoc warnings gate (rmg-geom @ $PINNED)"
RUSTDOCFLAGS="-D warnings" cargo +"$PINNED" doc -p rmg-geom --no-deps

# Banned patterns
echo "[pre-push] scanning banned patterns"
# Match any crate-level allow(...) that includes missing_docs; exclude telemetry.rs explicitly
if rg -n '#!\[allow\([^]]*missing_docs[^]]*\)\]' --glob '!crates/rmg-core/src/telemetry.rs' crates >/dev/null; then
# Forbid crate-level allow(missing_docs) in library source files, but allow in tests and build scripts
if rg -n '#!\[allow\([^]]*missing_docs[^]]*\)\]' \
crates \
--glob 'crates/**/src/**/*.rs' \
--glob '!**/telemetry.rs' \
--glob '!**/tests/**' \
--glob '!**/build.rs' >/dev/null; then
echo "pre-push: crate-level allow(missing_docs) is forbidden (except telemetry.rs)." >&2
rg -n '#!\[allow\([^]]*missing_docs[^]]*\)\]' --glob '!crates/rmg-core/src/telemetry.rs' crates | cat >&2 || true
rg -n '#!\[allow\([^]]*missing_docs[^]]*\)\]' crates \
--glob 'crates/**/src/**/*.rs' \
--glob '!**/telemetry.rs' \
--glob '!**/tests/**' \
--glob '!**/build.rs' | cat >&2 || true
exit 1
fi
if rg -n "\#\[unsafe\(no_mangle\)\]" crates >/dev/null; then
Expand Down
44 changes: 6 additions & 38 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: false
- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@1.71.1
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
Expand All @@ -31,19 +30,14 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: false
- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@1.71.1
with:
toolchain: stable
components: clippy
- name: rustup override stable
run: rustup toolchain install stable && rustup override set stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
- name: cargo clippy
env:
RUSTUP_TOOLCHAIN: stable
run: cargo clippy --all-targets -- -D warnings -D missing_docs

test:
Expand All @@ -53,19 +47,13 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: false
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: rustup override stable
run: rustup toolchain install stable && rustup override set stable
- uses: dtolnay/rust-toolchain@1.71.1
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
- name: cargo test
env:
RUSTUP_TOOLCHAIN: stable
run: cargo test
- name: cargo test (workspace)
run: cargo test --workspace
- name: PRNG golden regression (rmg-core)
run: cargo test -p rmg-core --features golden_prng -- tests::next_int_golden_regression

Expand Down Expand Up @@ -101,20 +89,6 @@ jobs:
exit 1;
}

msrv:
name: MSRV (rmg-core @ 1.68)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
- uses: dtolnay/rust-toolchain@1.68.0
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
- name: cargo check (rmg-core)
run: cargo check -p rmg-core --all-targets

rustdoc:
name: Rustdoc (rmg-core warnings gate)
Expand All @@ -123,13 +97,7 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: false
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: rustup override stable
run: rustup toolchain install stable && rustup override set stable
- uses: dtolnay/rust-toolchain@1.71.1
- uses: Swatinem/rust-cache@v2
- name: rustdoc warnings gate
env:
RUSTUP_TOOLCHAIN: stable
run: RUSTDOCFLAGS="-D warnings" cargo doc -p rmg-core --no-deps
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Echo is a deterministic, renderer-agnostic engine. We prioritize:
3. Review `AGENTS.md` for collaboration norms before touching runtime code.
4. Optional: develop inside the devcontainer for toolchain parity with CI.
- Open in VS Code → "Reopen in Container" (requires the Dev Containers extension).
- The container includes Rust stable + MSRV toolchains, clippy/rustfmt, Node, and gh.
- Post-create installs MSRV 1.68.0 and wasm target.
- The container includes Rust 1.71.1 (via rust-toolchain.toml), clippy/rustfmt, Node, and gh.
- Post-create installs toolchain 1.71.1 (no override); wasm32 target and components are added to 1.71.1.

## Branching & Workflow
- Keep `main` pristine. Create feature branches like `echo/<feature>` or `timeline/<experiment>`.
Expand Down
2 changes: 1 addition & 1 deletion crates/rmg-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "rmg-cli"
version = "0.1.0"
edition = "2021"
rust-version = "1.68"
rust-version = "1.71.1"
description = "Echo CLI: demos, benches, inspector launcher (future)"
license = "Apache-2.0"
repository = "https://github.com/flyingrobots/echo"
Expand Down
4 changes: 2 additions & 2 deletions crates/rmg-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "rmg-core"
version = "0.1.0"
edition = "2021"
rust-version = "1.68"
rust-version = "1.71.1"
description = "Echo core: deterministic typed graph rewriting engine"
license = "Apache-2.0"
repository = "https://github.com/flyingrobots/echo"
Expand All @@ -18,9 +18,9 @@ thiserror = "1.0"
hex = { version = "0.4", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
once_cell = "1.19"

[dev-dependencies]
once_cell = "1.19"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Expand Down
19 changes: 19 additions & 0 deletions crates/rmg-core/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Canonical digests and constants used across the engine.
use once_cell::sync::Lazy;

use crate::ident::Hash;

/// BLAKE3 digest of an empty byte slice.
///
/// Used where canonical empty input semantics are required.
pub static BLAKE3_EMPTY: Lazy<Hash> = Lazy::new(|| blake3::hash(&[]).into());

/// Canonical digest representing an empty length-prefix list: BLAKE3 of
/// `0u64.to_le_bytes()`.
///
/// Used for plan/decision/rewrites digests when the corresponding list is empty.
pub static DIGEST_LEN0_U64: Lazy<Hash> = Lazy::new(|| {
let mut h = blake3::Hasher::new();
h.update(&0u64.to_le_bytes());
h.finalize().into()
});
Loading