S4.7 — User motion validators in the bindings
Intent
Let Python and JS users validate a whole motion in one cross-language call (vectorised NumPy checks, physics-engine continuous collision, swept volumes) instead of one call per interpolated state. This is the main performance win of 0.9.0 for binding users.
Decisions (ADR-0003; grilling Q14)
- A user motion validator is a duck-typed object, not a callable and not a required base class: Python
check_motion(s1, s2) -> bool, JScheckMotion(s1, s2) -> boolean. - Verified when set (
set_motion_validator/ constructor kwarg): a missing method raisesConfigurationErrorimmediately, not mid-solve. - Later optional methods (e.g. a last-valid variant) are detected by presence (
hasattr/ property check) so they can be added without breaking anyone. - Strict return types: Python accepts
booland NumPybool_(pyo3 extraction already handles it); anything else, includingNone, raises. JS: non-boolean raises (replacing today'sconsole.warn+falsebehaviour). - One generic adapter per binding (via
PyStateConvert/JsStateConvert), not one per state type. - Type hints: Python
typing.Protocolstub (MotionValidator), TypeScriptinterface MotionValidator. - Exposing the built-in discrete validator as a wrappable object is not in scope (YAGNI; additive later).
Acceptance criteria
Source
Agreed in the senior-engineer grilling 2026-10-08 (decisions: repo docs/planning/adr/0003-motion-validation.md; glossary: CONTEXT.md). Regeneration spec: oxmpl - sprint-006. Line references are as of origin/main c558556 (2026-10-08), before E3. E3 renames things (rand Rng→RngExt, PyO3 with_gil→attach, edition 2024), so re-grep before trusting a line number.