From 0ee6523701d079fb95c7393d218704a4b44cef7b Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 01:23:24 -0800 Subject: [PATCH 1/8] docs(roadmap): add explicit success criteria per milestone and clarify refresh cadence; regen rollup --- docs/ROADMAP.md | 24 ++++++++++++------------ docs/echo-total.md | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 3c4ad3a..18d5d41 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -6,32 +6,32 @@ This roadmap reconciles our current plan with GitHub milestones, issues, and the ## Milestones -- M1 – Golden Tests +- M1 – Golden Tests (Target: CI gates operational; bit‑exact vectors validated) - Bit‑exact golden vectors for state_root/commit_id (genesis, merge, empty) - Math kernel goldens (rotation/multiply/sin/cos) - CI matrix: macOS + Ubuntu (glibc) + Alpine (musl) -- M2.0 – Scalar Foundation - - Scalar trait; F32Scalar deterministic wrappers; DFix64 Q32.32 - - Deterministic transcendentals (LUT + refinement); tables checked‑in +- M2.0 – Scalar Foundation (Target: det_fixed & det_float lanes green across OSes) + - Scalar trait; F32Scalar deterministic wrappers; DFix64 Q32.32 (fixed‑point 32.32 format) + - Deterministic transcendentals (LUT = lookup table + refinement); tables checked‑in - Motion rule → Scalar; v2 payload (6×i64 Q32.32), dual decode v1/v2 -- M2.1 – Lattice Joins - - Lattice trait; scheduler fold order - - Exemplar lattices: tags union, cap/max +- M2.1 – Lattice Joins (Target: replay‑invariant merges under ACI properties) + - Lattice trait; scheduler fold order (canonical) + - Exemplar lattices: tags union, cap/max (join keys documented) - ACI property + replay determinism tests -- M2.2 – Playground Slice +- M2.2 – Playground Slice (Target: demo + CLI show identical hashes under permutations) - Minimal WASM demo; CLI run/diff showing replay‑identical hashes -- M2.5 – Accumulator Joins +- M2.5 – Accumulator Joins (Target: delta‑style joins pass ACI/replay tests) - Delta‑style joins; deterministic rounding/saturation; ACI + replay -- M3 – Sweep‑and‑Prune v1 +- M3 – Sweep‑and‑Prune v1 (Target: deterministic broad‑phase replaces O(n²) baseline) - Integerized endpoints; stable tie‑breakers; ordering/stability property tests -- M4 – Determinism Proof & Publish 0.1 +- M4 – Determinism Proof & Publish 0.1 (Target: cross‑OS proof + 0.1 release) - Prove determinism across OSes; finalize docs; publish rmg‑core/geom 0.1 --- ## Issue Table (live snapshot) -Rows are GitHub issues. Priority/Estimate reflect Project 9 fields. Block/parent relationships use native GitHub issue dependencies; no custom text fields are used. +Rows are GitHub issues. Priority/Estimate reflect Project 9 fields. Block/parent relationships use native GitHub issue dependencies; no custom text fields are used. Refresh cadence: update weekly or before each planning cycle. | Issue Name | # | Milestone | Priority | Estimate | Blocked By | Blocking | Parent | Children | Remarks | | --- | ---: | --- | --- | --- | --- | --- | --- | --- | --- | diff --git a/docs/echo-total.md b/docs/echo-total.md index 3fbf868..58604a2 100644 --- a/docs/echo-total.md +++ b/docs/echo-total.md @@ -757,32 +757,32 @@ This roadmap reconciles our current plan with GitHub milestones, issues, and the ## Milestones -- M1 – Golden Tests +- M1 – Golden Tests (Target: CI gates operational; bit‑exact vectors validated) - Bit‑exact golden vectors for state_root/commit_id (genesis, merge, empty) - Math kernel goldens (rotation/multiply/sin/cos) - CI matrix: macOS + Ubuntu (glibc) + Alpine (musl) -- M2.0 – Scalar Foundation - - Scalar trait; F32Scalar deterministic wrappers; DFix64 Q32.32 - - Deterministic transcendentals (LUT + refinement); tables checked‑in +- M2.0 – Scalar Foundation (Target: det_fixed & det_float lanes green across OSes) + - Scalar trait; F32Scalar deterministic wrappers; DFix64 Q32.32 (fixed‑point 32.32 format) + - Deterministic transcendentals (LUT = lookup table + refinement); tables checked‑in - Motion rule → Scalar; v2 payload (6×i64 Q32.32), dual decode v1/v2 -- M2.1 – Lattice Joins - - Lattice trait; scheduler fold order - - Exemplar lattices: tags union, cap/max +- M2.1 – Lattice Joins (Target: replay‑invariant merges under ACI properties) + - Lattice trait; scheduler fold order (canonical) + - Exemplar lattices: tags union, cap/max (join keys documented) - ACI property + replay determinism tests -- M2.2 – Playground Slice +- M2.2 – Playground Slice (Target: demo + CLI show identical hashes under permutations) - Minimal WASM demo; CLI run/diff showing replay‑identical hashes -- M2.5 – Accumulator Joins +- M2.5 – Accumulator Joins (Target: delta‑style joins pass ACI/replay tests) - Delta‑style joins; deterministic rounding/saturation; ACI + replay -- M3 – Sweep‑and‑Prune v1 +- M3 – Sweep‑and‑Prune v1 (Target: deterministic broad‑phase replaces O(n²) baseline) - Integerized endpoints; stable tie‑breakers; ordering/stability property tests -- M4 – Determinism Proof & Publish 0.1 +- M4 – Determinism Proof & Publish 0.1 (Target: cross‑OS proof + 0.1 release) - Prove determinism across OSes; finalize docs; publish rmg‑core/geom 0.1 --- ## Issue Table (live snapshot) -Rows are GitHub issues. Priority/Estimate reflect Project 9 fields. Block/parent relationships use native GitHub issue dependencies; no custom text fields are used. +Rows are GitHub issues. Priority/Estimate reflect Project 9 fields. Block/parent relationships use native GitHub issue dependencies; no custom text fields are used. Refresh cadence: update weekly or before each planning cycle. | Issue Name | # | Milestone | Priority | Estimate | Blocked By | Blocking | Parent | Children | Remarks | | --- | ---: | --- | --- | --- | --- | --- | --- | --- | --- | From 31b2845cac04b43cca8d63410b2b3fbb1a5829db Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 01:34:43 -0800 Subject: [PATCH 2/8] chore(benches): allow missing docs in bench crate and fix unused mut; fmt via hook --- Cargo.lock | 209 ++++++++++++++++++ Cargo.toml | 3 +- crates/rmg-benches/Cargo.toml | 14 ++ .../rmg-benches/benches/motion_throughput.rs | 65 ++++++ docs/decision-log.md | 6 + docs/echo-total.md | 10 + docs/execution-plan.md | 4 + 7 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 crates/rmg-benches/Cargo.toml create mode 100644 crates/rmg-benches/benches/motion_throughput.rs diff --git a/Cargo.lock b/Cargo.lock index 07d3f53..f929fd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + [[package]] name = "arrayref" version = "0.3.9" @@ -66,6 +87,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.43" @@ -82,6 +109,58 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -98,6 +177,52 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "errno" version = "0.3.14" @@ -138,12 +263,49 @@ dependencies = [ "wasip2", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -203,6 +365,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -299,12 +467,43 @@ dependencies = [ "rand_core", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rmg-benches" +version = "0.1.0" +dependencies = [ + "criterion", + "rmg-core", +] + [[package]] name = "rmg-cli" version = "0.1.0" @@ -487,6 +686,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "unarray" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index f86bcb1..42c2550 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ members = [ "crates/rmg-ffi", "crates/rmg-wasm", "crates/rmg-cli", - "crates/rmg-geom" + "crates/rmg-geom", + "crates/rmg-benches" ] resolver = "2" diff --git a/crates/rmg-benches/Cargo.toml b/crates/rmg-benches/Cargo.toml new file mode 100644 index 0000000..068e3b1 --- /dev/null +++ b/crates/rmg-benches/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rmg-benches" +version = "0.1.0" +edition = "2021" +publish = false + +[dev-dependencies] +criterion = { version = "0.5", default-features = false, features = ["html_reports"] } +rmg-core = { path = "../rmg-core" } + +[[bench]] +name = "motion_throughput" +harness = false + diff --git a/crates/rmg-benches/benches/motion_throughput.rs b/crates/rmg-benches/benches/motion_throughput.rs new file mode 100644 index 0000000..2664d63 --- /dev/null +++ b/crates/rmg-benches/benches/motion_throughput.rs @@ -0,0 +1,65 @@ +#![allow(missing_docs)] +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; +use rmg_core::{ + decode_motion_payload, encode_motion_payload, make_node_id, make_type_id, ApplyResult, Engine, + NodeRecord, MOTION_RULE_NAME, +}; + +fn build_engine_with_n_entities(n: usize) -> (Engine, Vec) { + // Start from the demo engine (root + motion rule registered). + let mut engine = rmg_core::build_motion_demo_engine(); + let ty = make_type_id("entity"); + let mut labels = Vec::with_capacity(n); + // Insert N entities with a simple payload. + for i in 0..n { + let label = format!("ent-{}", i); + let id = make_node_id(&label); + let pos = [i as f32, 0.0, 0.0]; + let vel = [1.0, 0.0, 0.0]; + let payload = encode_motion_payload(pos, vel); + engine.insert_node( + id, + NodeRecord { + ty, + payload: Some(payload), + }, + ); + labels.push(label); + } + (engine, labels) +} + +fn bench_motion_apply(c: &mut Criterion) { + let mut group = c.benchmark_group("motion_apply"); + for &n in &[1usize, 10, 100, 1_000] { + group.throughput(Throughput::Elements(n as u64)); + group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { + b.iter_batched( + || { + let (engine, labels) = build_engine_with_n_entities(n); + (engine, labels) + }, + |(mut engine, labels)| { + // Apply motion once to each entity. + let tx = engine.begin(); + for label in &labels { + let id = make_node_id(label); + let res = engine.apply(tx, MOTION_RULE_NAME, &id).expect("apply"); + assert!(matches!(res, ApplyResult::Applied | ApplyResult::NoMatch)); + } + engine.commit(tx).expect("commit"); + + // Quick decode sanity for the first entity to keep benchmark honest. + let first = make_node_id(&labels[0]); + let node = engine.node(&first).expect("node exists"); + let _ = decode_motion_payload(node.payload.as_ref().expect("payload")); + }, + BatchSize::PerIteration, + ) + }); + } + group.finish(); +} + +criterion_group!(benches, bench_motion_apply); +criterion_main!(benches); diff --git a/docs/decision-log.md b/docs/decision-log.md index 78c396f..1204572 100644 --- a/docs/decision-log.md +++ b/docs/decision-log.md @@ -147,6 +147,12 @@ The following entries use a heading + bullets format for richer context. | 2025-10-30 | Spec + lint hygiene | Removed duplicate `clippy::module_name_repetitions` allow in `rmg-core/src/lib.rs`. Clarified `docs/spec-merkle-commit.md`: `edge_count` is u64 LE and may be 0; genesis commits have length=0 parents; “empty digest” explicitly defined as `blake3(b"")`; v1 mandates empty `decision_digest` until Aion lands. | Codifies intent; prevents ambiguity for implementers. | No code behavior changes; spec is clearer. | | 2025-10-30 | Templates & Project | Added issue/PR/RFC templates and configured Echo Project (Status: Blocked/Ready/Done); fixed YAML lint nits | Streamlines review process and Kanban tracking | No runtime impact; CI docs guard satisfied | +## 2025-11-02 — M1: benches crate skeleton (PR-11) + +- Decision: Add `crates/rmg-benches` with a minimal Criterion harness and a motion-throughput benchmark using public `rmg-core` APIs. +- Rationale: Establish a place for performance microbenches; keep PR small and focused before adding JSON artifacts/regression gates in follow-ups. +- Consequence: Benches run locally via `cargo bench -p rmg-benches`; no runtime changes. + ## 2025-11-01 — Docs rollup automation (pre-commit + subdirs) - Context: CI rollup check fails if `docs/echo-total.md` drifts; authors asked to trigger the rollup automatically on local commits and include subdirectories. diff --git a/docs/echo-total.md b/docs/echo-total.md index 58604a2..c08def4 100644 --- a/docs/echo-total.md +++ b/docs/echo-total.md @@ -536,6 +536,10 @@ Remember: every entry here shrinks temporal drift between Codices. Leave breadcr > 2025-11-02 — Hotfix follow-up: tighter normalization + annotation - CI normalization now only removes `Generated` header lines in the top-of-file header block (from start to first blank line) and tolerates whitespace/case variants and legacy forms like `Generated:`, `generated at:`, `Generated by:`. Added a GitHub Actions annotation on failure to point directly at `docs/echo-total.md`. +> 2025-11-02 — PR-11: benches crate skeleton (M1) + +- Add `crates/rmg-benches` with Criterion harness and a minimal motion-throughput benchmark that exercises public `rmg-core` APIs. +- Scope: benches-only; no runtime changes. Document local run (`cargo bench -p rmg-benches`). --- @@ -692,6 +696,12 @@ The following entries use a heading + bullets format for richer context. | 2025-10-30 | Spec + lint hygiene | Removed duplicate `clippy::module_name_repetitions` allow in `rmg-core/src/lib.rs`. Clarified `docs/spec-merkle-commit.md`: `edge_count` is u64 LE and may be 0; genesis commits have length=0 parents; “empty digest” explicitly defined as `blake3(b"")`; v1 mandates empty `decision_digest` until Aion lands. | Codifies intent; prevents ambiguity for implementers. | No code behavior changes; spec is clearer. | | 2025-10-30 | Templates & Project | Added issue/PR/RFC templates and configured Echo Project (Status: Blocked/Ready/Done); fixed YAML lint nits | Streamlines review process and Kanban tracking | No runtime impact; CI docs guard satisfied | +## 2025-11-02 — M1: benches crate skeleton (PR-11) + +- Decision: Add `crates/rmg-benches` with a minimal Criterion harness and a motion-throughput benchmark using public `rmg-core` APIs. +- Rationale: Establish a place for performance microbenches; keep PR small and focused before adding JSON artifacts/regression gates in follow-ups. +- Consequence: Benches run locally via `cargo bench -p rmg-benches`; no runtime changes. + ## 2025-11-01 — Docs rollup automation (pre-commit + subdirs) - Context: CI rollup check fails if `docs/echo-total.md` drifts; authors asked to trigger the rollup automatically on local commits and include subdirectories. diff --git a/docs/execution-plan.md b/docs/execution-plan.md index 22b7eaa..861260d 100644 --- a/docs/execution-plan.md +++ b/docs/execution-plan.md @@ -309,3 +309,7 @@ Remember: every entry here shrinks temporal drift between Codices. Leave breadcr > 2025-11-02 — Hotfix follow-up: tighter normalization + annotation - CI normalization now only removes `Generated` header lines in the top-of-file header block (from start to first blank line) and tolerates whitespace/case variants and legacy forms like `Generated:`, `generated at:`, `Generated by:`. Added a GitHub Actions annotation on failure to point directly at `docs/echo-total.md`. +> 2025-11-02 — PR-11: benches crate skeleton (M1) + +- Add `crates/rmg-benches` with Criterion harness and a minimal motion-throughput benchmark that exercises public `rmg-core` APIs. +- Scope: benches-only; no runtime changes. Document local run (`cargo bench -p rmg-benches`). From d69f8ea8b737a9e32e704ac72c88e7495d5f493f Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 01:59:05 -0800 Subject: [PATCH 3/8] ci(deny): benches license + no wildcard; docs rollup regen --- Cargo.lock | 1 + crates/rmg-benches/Cargo.toml | 2 +- docs/decision-log.md | 7 +++++++ docs/echo-total.md | 15 +++++++++++++++ docs/execution-plan.md | 8 ++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f929fd8..d03903d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,6 +500,7 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" name = "rmg-benches" version = "0.1.0" dependencies = [ + "blake3", "criterion", "rmg-core", ] diff --git a/crates/rmg-benches/Cargo.toml b/crates/rmg-benches/Cargo.toml index 068e3b1..490f472 100644 --- a/crates/rmg-benches/Cargo.toml +++ b/crates/rmg-benches/Cargo.toml @@ -3,6 +3,7 @@ name = "rmg-benches" version = "0.1.0" edition = "2021" publish = false +license = "Apache-2.0" [dev-dependencies] criterion = { version = "0.5", default-features = false, features = ["html_reports"] } @@ -11,4 +12,3 @@ rmg-core = { path = "../rmg-core" } [[bench]] name = "motion_throughput" harness = false - diff --git a/docs/decision-log.md b/docs/decision-log.md index 1204572..d145b59 100644 --- a/docs/decision-log.md +++ b/docs/decision-log.md @@ -175,3 +175,10 @@ The following entries use a heading + bullets format for richer context. - Decision: Limit normalization to the header region only and accept case/whitespace/legacy variants (`Generated:`, `generated at:`, `Generated by:`). Emit a GitHub Actions `::error` annotation targeting `docs/echo-total.md` when differences remain to improve diagnostics. - Consequence: Clearer CI failures; minimal, targeted normalization avoids masking content issues. + +## 2025-11-02 — CI hotfix: cargo-deny (benches) + +- Context: CI `cargo-deny` job failed on PR-11 due to `rmg-benches` lacking a license and a prior wildcard dependency reference reported by CI logs. +- Decision: Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml` and ensure `rmg-core` is referenced via a path dev-dependency (no wildcard). +- Rationale: Keep workspace policy consistent with other crates (Apache-2.0) and satisfy bans (wildcards = deny) and licenses checks. +- Consequence: `cargo-deny` bans/licenses should pass; remaining warnings are deprecations in `deny.toml` to be addressed in a later sweep. diff --git a/docs/echo-total.md b/docs/echo-total.md index c08def4..e88f0a4 100644 --- a/docs/echo-total.md +++ b/docs/echo-total.md @@ -260,6 +260,14 @@ This is Codex’s working map for building Echo. Update it relentlessly—each s ## Today’s Intent +> 2025-11-02 — PR-11 hotfix-deterministic-rollup-check + +- Switch to `echo/hotfix-deterministic-rollup-check`, fetch and merge `origin/main` (merge commit; no rebase). +- Fix CI cargo-deny failures: + - Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml`. + - Ensure no wildcard dependency remains in benches (use workspace path dep for `rmg-core`). +- Keep `deny.toml` policy intact; handle deprecation warnings in a later sweep. + > 2025-10-30 — PR-01: Golden motion fixtures (tests-only) - Add JSON golden fixtures and a minimal harness for the motion rule under `crates/rmg-core/tests/`. @@ -725,6 +733,13 @@ The following entries use a heading + bullets format for richer context. - Decision: Limit normalization to the header region only and accept case/whitespace/legacy variants (`Generated:`, `generated at:`, `Generated by:`). Emit a GitHub Actions `::error` annotation targeting `docs/echo-total.md` when differences remain to improve diagnostics. - Consequence: Clearer CI failures; minimal, targeted normalization avoids masking content issues. +## 2025-11-02 — CI hotfix: cargo-deny (benches) + +- Context: CI `cargo-deny` job failed on PR-11 due to `rmg-benches` lacking a license and a prior wildcard dependency reference reported by CI logs. +- Decision: Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml` and ensure `rmg-core` is referenced via a path dev-dependency (no wildcard). +- Rationale: Keep workspace policy consistent with other crates (Apache-2.0) and satisfy bans (wildcards = deny) and licenses checks. +- Consequence: `cargo-deny` bans/licenses should pass; remaining warnings are deprecations in `deny.toml` to be addressed in a later sweep. + --- diff --git a/docs/execution-plan.md b/docs/execution-plan.md index 861260d..b269a84 100644 --- a/docs/execution-plan.md +++ b/docs/execution-plan.md @@ -33,6 +33,14 @@ This is Codex’s working map for building Echo. Update it relentlessly—each s ## Today’s Intent +> 2025-11-02 — PR-11 hotfix-deterministic-rollup-check + +- Switch to `echo/hotfix-deterministic-rollup-check`, fetch and merge `origin/main` (merge commit; no rebase). +- Fix CI cargo-deny failures: + - Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml`. + - Ensure no wildcard dependency remains in benches (use workspace path dep for `rmg-core`). +- Keep `deny.toml` policy intact; handle deprecation warnings in a later sweep. + > 2025-10-30 — PR-01: Golden motion fixtures (tests-only) - Add JSON golden fixtures and a minimal harness for the motion rule under `crates/rmg-core/tests/`. From b942ba82d5e98cac98158da4b359529beccf7d95 Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 02:02:31 -0800 Subject: [PATCH 4/8] ci(deny): modernize deny.toml; docs rollup regen --- Cargo.lock | 1 - deny.toml | 13 +++++++------ docs/decision-log.md | 7 +++++++ docs/echo-total.md | 9 ++++++++- docs/execution-plan.md | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d03903d..f929fd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,7 +500,6 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" name = "rmg-benches" version = "0.1.0" dependencies = [ - "blake3", "criterion", "rmg-core", ] diff --git a/deny.toml b/deny.toml index 7099e14..ae8143d 100644 --- a/deny.toml +++ b/deny.toml @@ -16,11 +16,13 @@ allow = [ "Unlicense", ] -# Disallow copyleft by default; we do not expect GPL-family in the runtime. -copyleft = "deny" - -# Treat unknown or missing license as a failure. -unlicensed = "deny" +# Modernized for cargo-deny >=0.14.21 (PR #611): +# - Removed deprecated keys `copyleft` and `unlicensed`. +# Copyleft licenses remain effectively denied by virtue of the explicit +# `allow = [...]` list above. +# Crates missing a license will be flagged by cargo-deny as issues; our +# workspace crates all declare licenses, so no explicit `unlicensed` policy +# is required here. # Confidence threshold for license text detection (default 0.8) confidence-threshold = 0.8 @@ -33,4 +35,3 @@ wildcards = "deny" [sources] unknown-registry = "deny" unknown-git = "deny" - diff --git a/docs/decision-log.md b/docs/decision-log.md index d145b59..4f10cee 100644 --- a/docs/decision-log.md +++ b/docs/decision-log.md @@ -182,3 +182,10 @@ The following entries use a heading + bullets format for richer context. - Decision: Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml` and ensure `rmg-core` is referenced via a path dev-dependency (no wildcard). - Rationale: Keep workspace policy consistent with other crates (Apache-2.0) and satisfy bans (wildcards = deny) and licenses checks. - Consequence: `cargo-deny` bans/licenses should pass; remaining warnings are deprecations in `deny.toml` to be addressed in a later sweep. + +## 2025-11-02 — cargo-deny modernization + +- Context: CI emitted deprecation warnings for `copyleft` and `unlicensed` keys in `deny.toml` (cargo-deny PR #611). +- Decision: Remove deprecated keys; rely on the explicit permissive `allow = [...]` list to exclude copyleft licenses; ensure all workspace crates declare a license (benches fixed earlier). +- Rationale: Keep CI quiet and align with current cargo-deny schema without weakening enforcement. +- Consequence: Same effective policy, no deprecation warnings; future license exceptions remain possible via standard cargo-deny mechanisms. diff --git a/docs/echo-total.md b/docs/echo-total.md index e88f0a4..86b44ca 100644 --- a/docs/echo-total.md +++ b/docs/echo-total.md @@ -266,7 +266,7 @@ This is Codex’s working map for building Echo. Update it relentlessly—each s - Fix CI cargo-deny failures: - Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml`. - Ensure no wildcard dependency remains in benches (use workspace path dep for `rmg-core`). -- Keep `deny.toml` policy intact; handle deprecation warnings in a later sweep. +- Modernize `deny.toml` (remove deprecated `copyleft` and `unlicensed` keys per cargo-deny PR #611); enforcement still via explicit allowlist. > 2025-10-30 — PR-01: Golden motion fixtures (tests-only) @@ -740,6 +740,13 @@ The following entries use a heading + bullets format for richer context. - Rationale: Keep workspace policy consistent with other crates (Apache-2.0) and satisfy bans (wildcards = deny) and licenses checks. - Consequence: `cargo-deny` bans/licenses should pass; remaining warnings are deprecations in `deny.toml` to be addressed in a later sweep. +## 2025-11-02 — cargo-deny modernization + +- Context: CI emitted deprecation warnings for `copyleft` and `unlicensed` keys in `deny.toml` (cargo-deny PR #611). +- Decision: Remove deprecated keys; rely on the explicit permissive `allow = [...]` list to exclude copyleft licenses; ensure all workspace crates declare a license (benches fixed earlier). +- Rationale: Keep CI quiet and align with current cargo-deny schema without weakening enforcement. +- Consequence: Same effective policy, no deprecation warnings; future license exceptions remain possible via standard cargo-deny mechanisms. + --- diff --git a/docs/execution-plan.md b/docs/execution-plan.md index b269a84..93eaf53 100644 --- a/docs/execution-plan.md +++ b/docs/execution-plan.md @@ -39,7 +39,7 @@ This is Codex’s working map for building Echo. Update it relentlessly—each s - Fix CI cargo-deny failures: - Add `license = "Apache-2.0"` to `crates/rmg-benches/Cargo.toml`. - Ensure no wildcard dependency remains in benches (use workspace path dep for `rmg-core`). -- Keep `deny.toml` policy intact; handle deprecation warnings in a later sweep. +- Modernize `deny.toml` (remove deprecated `copyleft` and `unlicensed` keys per cargo-deny PR #611); enforcement still via explicit allowlist. > 2025-10-30 — PR-01: Golden motion fixtures (tests-only) From 0852b2855f9f6ca2e02d2c95db03bb2f832c8237 Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 03:34:11 -0800 Subject: [PATCH 5/8] bench: stabilize motion_throughput and remove hashing from hot loop\n\n- Precompute NodeId in setup; no hashing in measured loop\n- Use debug_assert! in hot path\n- Decode + validate payload and black_box result\n- Set Criterion group sample_size(50) and warm_up_time(3s)\n- Add [profile.bench] tuned for throughput\n\nRefs PR-11 --- Cargo.toml | 7 ++++ .../rmg-benches/benches/motion_throughput.rs | 40 ++++++++++++------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42c2550..abcdcb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,10 @@ opt-level = "s" lto = true codegen-units = 1 strip = true + +[profile.bench] +# Derive from release but optimize for throughput and faster builds +opt-level = 3 +lto = false +codegen-units = 16 +debug = true diff --git a/crates/rmg-benches/benches/motion_throughput.rs b/crates/rmg-benches/benches/motion_throughput.rs index 2664d63..f6e7e7d 100644 --- a/crates/rmg-benches/benches/motion_throughput.rs +++ b/crates/rmg-benches/benches/motion_throughput.rs @@ -2,17 +2,19 @@ use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; use rmg_core::{ decode_motion_payload, encode_motion_payload, make_node_id, make_type_id, ApplyResult, Engine, - NodeRecord, MOTION_RULE_NAME, + NodeId, NodeRecord, MOTION_RULE_NAME, }; +use std::{hint::black_box, time::Duration}; -fn build_engine_with_n_entities(n: usize) -> (Engine, Vec) { +fn build_engine_with_n_entities(n: usize) -> (Engine, Vec) { // Start from the demo engine (root + motion rule registered). let mut engine = rmg_core::build_motion_demo_engine(); let ty = make_type_id("entity"); - let mut labels = Vec::with_capacity(n); + let mut ids = Vec::with_capacity(n); // Insert N entities with a simple payload. for i in 0..n { let label = format!("ent-{}", i); + // Precompute NodeId so hashing is not part of the hot loop. let id = make_node_id(&label); let pos = [i as f32, 0.0, 0.0]; let vel = [1.0, 0.0, 0.0]; @@ -24,35 +26,43 @@ fn build_engine_with_n_entities(n: usize) -> (Engine, Vec) { payload: Some(payload), }, ); - labels.push(label); + ids.push(id); } - (engine, labels) + (engine, ids) } fn bench_motion_apply(c: &mut Criterion) { let mut group = c.benchmark_group("motion_apply"); + // Stabilize measurements: fixed warmup and sample size. + group.sample_size(50); + group.warm_up_time(Duration::from_secs(3)); for &n in &[1usize, 10, 100, 1_000] { group.throughput(Throughput::Elements(n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { b.iter_batched( || { - let (engine, labels) = build_engine_with_n_entities(n); - (engine, labels) + let (engine, ids) = build_engine_with_n_entities(n); + (engine, ids) }, - |(mut engine, labels)| { + |(mut engine, ids)| { // Apply motion once to each entity. let tx = engine.begin(); - for label in &labels { - let id = make_node_id(label); - let res = engine.apply(tx, MOTION_RULE_NAME, &id).expect("apply"); - assert!(matches!(res, ApplyResult::Applied | ApplyResult::NoMatch)); + for id in &ids { + let res = engine.apply(tx, MOTION_RULE_NAME, id).expect("apply"); + // Avoid penalizing release runs. + debug_assert!(matches!(res, ApplyResult::Applied | ApplyResult::NoMatch)); } engine.commit(tx).expect("commit"); - // Quick decode sanity for the first entity to keep benchmark honest. - let first = make_node_id(&labels[0]); + // Decode and validate the first entity's payload to keep work observable, + // then black_box to prevent the optimizer from eliminating it. + let first = ids[0]; let node = engine.node(&first).expect("node exists"); - let _ = decode_motion_payload(node.payload.as_ref().expect("payload")); + let decoded = decode_motion_payload(node.payload.as_ref().expect("payload")) + .expect("decode"); + debug_assert!(decoded.0.iter().all(|v| v.is_finite())); + debug_assert!(decoded.1.iter().all(|v| v.is_finite())); + black_box(decoded); }, BatchSize::PerIteration, ) From 4ebdf8d53be1f4ddf5103723ed72c4da0ed0d840 Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 03:39:02 -0800 Subject: [PATCH 6/8] deny(bans): pin rmg-core version in benches dev-deps to avoid wildcard detection --- crates/rmg-benches/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rmg-benches/Cargo.toml b/crates/rmg-benches/Cargo.toml index 490f472..24ee0a4 100644 --- a/crates/rmg-benches/Cargo.toml +++ b/crates/rmg-benches/Cargo.toml @@ -7,7 +7,8 @@ license = "Apache-2.0" [dev-dependencies] criterion = { version = "0.5", default-features = false, features = ["html_reports"] } -rmg-core = { path = "../rmg-core" } +# Pin version alongside path to satisfy cargo-deny wildcard bans +rmg-core = { version = "0.1.0", path = "../rmg-core" } [[bench]] name = "motion_throughput" From d58e1de657c1c40a62b940e3e13310b26fb64872 Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 04:43:08 -0800 Subject: [PATCH 7/8] bench: split into build-only and apply+commit; add measurement_time + noise_threshold; update README Benchmarks; regen docs rollup --- Cargo.toml | 2 + README.md | 6 ++ .../rmg-benches/benches/motion_throughput.rs | 56 +++++++++++++------ docs/.vitepress/config.ts | 6 +- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index abcdcb4..27073e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,5 @@ opt-level = 3 lto = false codegen-units = 16 debug = true +panic = "abort" +strip = false diff --git a/README.md b/README.md index 86043be..8d4616b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,12 @@ RMG provides atomic, in-place edits of recursive meta-graphs with deterministic It’s the core of the Echo engine: runtime, assets, networking, and tools all operate on the same living graph of graphs. +## Developer: Running Benchmarks + +- Command: `cargo bench -p rmg-benches` +- Purpose: Runs Criterion micro-benchmarks for the benches crate (`crates/rmg-benches`). +- Location: see `crates/rmg-benches/` for sources and configuration. + ### Core Principles | Principle | Description | diff --git a/crates/rmg-benches/benches/motion_throughput.rs b/crates/rmg-benches/benches/motion_throughput.rs index f6e7e7d..ccbd08a 100644 --- a/crates/rmg-benches/benches/motion_throughput.rs +++ b/crates/rmg-benches/benches/motion_throughput.rs @@ -32,32 +32,54 @@ fn build_engine_with_n_entities(n: usize) -> (Engine, Vec) { } fn bench_motion_apply(c: &mut Criterion) { - let mut group = c.benchmark_group("motion_apply"); - // Stabilize measurements: fixed warmup and sample size. - group.sample_size(50); - group.warm_up_time(Duration::from_secs(3)); + // Bench 1: Build-only — measures engine construction + inserts. + let mut build_group = c.benchmark_group("motion_build_only"); + build_group.sample_size(50); + build_group.warm_up_time(Duration::from_secs(3)); + build_group.measurement_time(Duration::from_secs(6)); + build_group.noise_threshold(0.02); for &n in &[1usize, 10, 100, 1_000] { - group.throughput(Throughput::Elements(n as u64)); - group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { + build_group.throughput(Throughput::Elements(n as u64)); + build_group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { + // Measure just the build; keep work observable via black_box. + b.iter(|| { + let (engine, ids) = build_engine_with_n_entities(n); + // Optional quick sanity on the first entity to keep side effects visible. + let node = engine.node(&ids[0]).expect("node exists"); + let decoded = + decode_motion_payload(node.payload.as_ref().expect("payload")).expect("decode"); + debug_assert!(decoded.0.iter().all(|v| v.is_finite())); + debug_assert!(decoded.1.iter().all(|v| v.is_finite())); + black_box(engine); + black_box(ids); + black_box(decoded); + }) + }); + } + build_group.finish(); + + // Bench 2: Apply+Commit — measure only the rewrite/commit path. + let mut apply_group = c.benchmark_group("motion_apply_commit"); + apply_group.sample_size(50); + apply_group.warm_up_time(Duration::from_secs(3)); + apply_group.measurement_time(Duration::from_secs(6)); + apply_group.noise_threshold(0.02); + for &n in &[1usize, 10, 100, 1_000] { + apply_group.throughput(Throughput::Elements(n as u64)); + apply_group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { + // Build a fresh engine/ids in setup (not timed), measure only the apply/commit work. b.iter_batched( - || { - let (engine, ids) = build_engine_with_n_entities(n); - (engine, ids) - }, + || build_engine_with_n_entities(n), |(mut engine, ids)| { - // Apply motion once to each entity. let tx = engine.begin(); for id in &ids { let res = engine.apply(tx, MOTION_RULE_NAME, id).expect("apply"); - // Avoid penalizing release runs. debug_assert!(matches!(res, ApplyResult::Applied | ApplyResult::NoMatch)); } engine.commit(tx).expect("commit"); - // Decode and validate the first entity's payload to keep work observable, - // then black_box to prevent the optimizer from eliminating it. - let first = ids[0]; - let node = engine.node(&first).expect("node exists"); + // Decode and validate the first entity's payload and black_box the result. + let node = engine.node(&ids[0]).expect("node exists"); let decoded = decode_motion_payload(node.payload.as_ref().expect("payload")) .expect("decode"); debug_assert!(decoded.0.iter().all(|v| v.is_finite())); @@ -68,7 +90,7 @@ fn bench_motion_apply(c: &mut Criterion) { ) }); } - group.finish(); + apply_group.finish(); } criterion_group!(benches, bench_motion_apply); diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index bfea9d7..4eed0d3 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'vitepress' export default defineConfig({ title: 'Echo', - description: 'Deterministic, multiverse-aware ECS', + description: 'Real-Time, Deterministic, Recursive Meta-Graph Simulation Engine', cleanUrls: true, ignoreDeadLinks: [ // Collision tour HTML is added in a separate PR @@ -25,7 +25,3 @@ export default defineConfig({ } } }) -<<<<<<< HEAD -======= - ->>>>>>> origin/main From 73c4d9cadd25f508c7dedec5e321384f12efdd63 Mon Sep 17 00:00:00 2001 From: "J. Kirby Ross" Date: Sun, 2 Nov 2025 04:43:44 -0800 Subject: [PATCH 8/8] docs: add CI note to Decision Log; regen rollup --- docs/decision-log.md | 1 + docs/echo-total.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/decision-log.md b/docs/decision-log.md index 4f10cee..dfc1e39 100644 --- a/docs/decision-log.md +++ b/docs/decision-log.md @@ -189,3 +189,4 @@ The following entries use a heading + bullets format for richer context. - Decision: Remove deprecated keys; rely on the explicit permissive `allow = [...]` list to exclude copyleft licenses; ensure all workspace crates declare a license (benches fixed earlier). - Rationale: Keep CI quiet and align with current cargo-deny schema without weakening enforcement. - Consequence: Same effective policy, no deprecation warnings; future license exceptions remain possible via standard cargo-deny mechanisms. +- CI Note: Use `cargo-deny >= 0.14.21` in CI (workflow/container) to avoid schema drift and deprecation surprises. Pin the action/image or the downloaded binary version accordingly. diff --git a/docs/echo-total.md b/docs/echo-total.md index 86b44ca..2a2db72 100644 --- a/docs/echo-total.md +++ b/docs/echo-total.md @@ -746,6 +746,7 @@ The following entries use a heading + bullets format for richer context. - Decision: Remove deprecated keys; rely on the explicit permissive `allow = [...]` list to exclude copyleft licenses; ensure all workspace crates declare a license (benches fixed earlier). - Rationale: Keep CI quiet and align with current cargo-deny schema without weakening enforcement. - Consequence: Same effective policy, no deprecation warnings; future license exceptions remain possible via standard cargo-deny mechanisms. +- CI Note: Use `cargo-deny >= 0.14.21` in CI (workflow/container) to avoid schema drift and deprecation surprises. Pin the action/image or the downloaded binary version accordingly. ---