feat(units): uom-backed newtypes + dim currency for typed math #37

Merged
patman merged 1 commit from patman/uom-units into main 2026-07-09 23:11:19 +00:00
Owner

What this does

Establishes a two-layer typed-units architecture, motivated by the upcoming (heavily AI-assisted) CAM build-out: compile-time dimensional analysis catches structurally-wrong equations (m/s + m, bad derivative chains) that tests only catch case-by-case.

  • Boundary newtypes (common::types::units: Length, Velocity, …) — wrap a uom Quantity (SI storage), stay operator-free with explicit from_*/as_* and CNC unit I/O (from_rpm, from_thou). Used for storage and edges (.roo/config/serialized state, public API).
  • dim computation currency (new module re-exporting uom quantities) — full dimension-checked arithmetic for the math/CAM/kinematics layer (Length / Time → Velocity; m/s + m won't compile).
  • Zero-cost to_uom() / from_uom() bridge on every scalar (the newtype wraps exactly the dim quantity, so conversion is a field move).
  • Matrices stay raw f64 (SI) in dirac — nalgebra needs the scalar closed under *, and Length*Length=Area breaks that. uom guards scalar formulas and kernel seams, not interiors. Documented as a caveat.
  • Updates the CLAUDE.md units policy to the two-layer model, and adds docs/dev-tips/uom.md (zensical) as the guardrail doc for CAM agents. dirac untouched.

Background

Replaces the original "uom hidden, no operators" framing of this PR. An adversarial review correctly noted that the opaque newtype gave no dimensional checking (no arithmetic). With CAM next and AI-driven, the fix is to expose uom as the computation currency rather than revert — this PR does that.

Stack position

PR 2 of 2. Based on #36 (wait-timeout lint fix), required so tests can run.

Verification

buck2 build //common:common green; buck2 test //common:common-tests85 passed, including a new currency_arithmetic_is_dimension_checked test exercising Length / Time → Velocity through the bridge.

Review focus

  • The two-layer split and the to_uom/from_uom bridge (common/src/types/units/{mod.rs,dim.rs}).
  • The si() struct-literal construction keeping const constructors (depends on uom field visibility — documented; loud failure if uom closes them).
  • The matrix caveat — confirm the boundary-vs-interior expectation is right for the planned Stewart/Jacobian work.
## What this does Establishes a **two-layer** typed-units architecture, motivated by the upcoming (heavily AI-assisted) CAM build-out: compile-time dimensional analysis catches structurally-wrong equations (`m/s + m`, bad derivative chains) that tests only catch case-by-case. - **Boundary newtypes** (`common::types::units`: `Length`, `Velocity`, …) — wrap a uom `Quantity` (SI storage), stay **operator-free** with explicit `from_*`/`as_*` and CNC unit I/O (`from_rpm`, `from_thou`). Used for storage and edges (`.roo`/config/serialized state, public API). - **`dim` computation currency** (new module re-exporting uom quantities) — full **dimension-checked arithmetic** for the math/CAM/kinematics layer (`Length / Time → Velocity`; `m/s + m` won't compile). - **Zero-cost `to_uom()` / `from_uom()` bridge** on every scalar (the newtype wraps exactly the `dim` quantity, so conversion is a field move). - **Matrices stay raw `f64` (SI) in dirac** — nalgebra needs the scalar closed under `*`, and `Length*Length=Area` breaks that. uom guards scalar formulas and kernel **seams**, not interiors. Documented as a caveat. - Updates the **CLAUDE.md** units policy to the two-layer model, and adds **`docs/dev-tips/uom.md`** (zensical) as the guardrail doc for CAM agents. `dirac` untouched. ## Background Replaces the original "uom hidden, no operators" framing of this PR. An adversarial review correctly noted that the opaque newtype gave no dimensional checking (no arithmetic). With CAM next and AI-driven, the fix is to *expose* uom as the computation currency rather than revert — this PR does that. ## Stack position PR 2 of 2. Based on #36 (wait-timeout lint fix), required so tests can run. ## Verification `buck2 build //common:common` green; `buck2 test //common:common-tests` → **85 passed**, including a new `currency_arithmetic_is_dimension_checked` test exercising `Length / Time → Velocity` through the bridge. ## Review focus - The two-layer split and the `to_uom`/`from_uom` bridge (`common/src/types/units/{mod.rs,dim.rs}`). - The `si()` struct-literal construction keeping `const` constructors (depends on uom field visibility — documented; loud failure if uom closes them). - The matrix caveat — confirm the boundary-vs-interior expectation is right for the planned Stewart/Jacobian work.
Replace the raw-f64 storage in the nine scalar quantity newtypes
(Length, Velocity, Acceleration, Jerk, Time, Angle, AngularVelocity,
AngularAcceleration, AngularJerk) with the corresponding uom Quantity,
so uom supplies compile-time dimensional checking and the unit
conversion factors. The public API is unchanged: same from_*/as_*
methods and const ZERO / const fn SI constructors (kept const via a
struct-literal helper, since uom::Quantity exposes its fields). Units
uom lacks (in/min, rev/s^2, rev/s^3) are composed dimensionally rather
than with hand-written factors. uom stays an implementation detail and
is never exposed in the public API. dirac is untouched.

Co-authored-by: patman-assist <patrick-ai@kgroo.co>
patman force-pushed patman/uom-units from 6a34c20634 to 50c340455b 2026-06-27 05:10:00 +00:00 Compare
patman changed title from feat(units): back unit newtypes with uom for dimensional checking to feat(units): uom-backed newtypes + dim currency for typed math 2026-06-27 05:10:30 +00:00
patman force-pushed patman/uom-units from 50c340455b to da93205d56 2026-06-27 06:18:53 +00:00 Compare
Collaborator

Hardened after a second adversarial review (force-pushed):

  • Documented uom's AngleKind limitation (the angular chain needs .into()) across dim.rs, mod.rs, CLAUDE.md, and docs/dev-tips/uom.md — previously the docs overstated "operators just work".
  • dim now re-exports base SI units (dim::unit) so the currency is self-contained.
  • Documented the dim::Length vs newtype Length collision rule (never glob both).
  • Added a compile_fail doctest for m/s + m and a passing angular-chain (.into()) test; re-scoped the misleading "dimension-checked" test comment.
  • Merged the double impl blocks per scalar.

Note: the compile_fail doctest documents the negative case but is not executed by the buck2 rust_test target (doctests aren't run there). Hard CI enforcement of compile-fail would need a trybuild dev-dep — deferred pending dep approval. Typed angular helpers split into #38.

Hardened after a second adversarial review (force-pushed): - Documented uom's `AngleKind` limitation (the angular chain needs `.into()`) across `dim.rs`, `mod.rs`, CLAUDE.md, and `docs/dev-tips/uom.md` — previously the docs overstated "operators just work". - `dim` now re-exports base SI units (`dim::unit`) so the currency is self-contained. - Documented the `dim::Length` vs newtype `Length` collision rule (never glob both). - Added a `compile_fail` doctest for `m/s + m` and a passing angular-chain (`.into()`) test; re-scoped the misleading "dimension-checked" test comment. - Merged the double `impl` blocks per scalar. Note: the `compile_fail` doctest documents the negative case but is not executed by the buck2 `rust_test` target (doctests aren't run there). Hard CI enforcement of compile-fail would need a `trybuild` dev-dep — deferred pending dep approval. Typed angular helpers split into #38.
patman changed target branch from patman/wait-timeout-cap-lints to main 2026-07-09 23:11:18 +00:00
patman merged commit dc1c927aea into main 2026-07-09 23:11:19 +00:00
Sign in to join this conversation.
No description provided.