S1.2 — Sampling-error propagation in all planners

Intent

All four planners (RRT, RRTConnect, RRTStar, PRM) call .unwrap() on sample_uniform / sample_goal; a systematic sampling failure panics instead of returning Err. Errors are systematic (bad space configuration), so retrying just burns timeout — propagate instead, via a new PlanningError::Sampling(StateSamplingError) variant (grilling decision 2026-09-22).

Design decisions (2026-09-22)

Parked explicitly (not this story): pd.start_states[0] indexing panic in setup() (empty start_states — real panic class but not sampling; would need a setup failure channel, the trait fork we declined; PlanningError::InvalidStartState exists for whoever takes it).

Scope additions (recorded per scope-change rule)

Acceptance criteria

Tasks

Bindings subtask scope — 2026-10-05

Owner requested a one-at-a-time breakdown of the senior-engineer hand-off. B1–B5 complete the Python slice; B6–B10 complete the JS/WASM slice; B11 is the conditional documentation check. These are checklist tasks inside S1.2, not separate stories or task files. Report progress using the B IDs so only verified items are checked off.

Shared test contract: use a one-dimensional unbounded RealVectorStateSpace, a fixed valid start, an always-valid checker, and a working fixed goal sampler. Tree planners use goal bias 0.0 and a positive solve budget, but sampling must fail immediately. Assert a normal Python Exception or the existing JS thrown-string behavior with message text identifying the sampling failure and unbounded dimension 0; an arbitrary exception/trap is not a pass. Match meaningful fragments rather than the complete Display sentence. Tests exercise the actual rebuilt language bindings, not Rust-only wrappers.

Scope is eight sampling-error cases, one per planner per binding, in one small test file per language. Existing generic binding conversions are expected to suffice; production binding edits are only for an actual defect found by these tests. New typed error APIs, planner × state-space matrices, time-budgeted roadmap construction, and additional goal-callback failure tests are outside this committed minimum. The full Rust/Python/JS test matrix remains the separate existing final-gate task; these focused checks do not close it. No story or sprint was added or swapped.

Failure-path validation — 2026-10-05

cargo test -p oxmpl --lib test_failure_path in the improve-stabilty-robustness worktree: all four module-local tests pass (4 passed, 0 failed; 0.00s test execution). Tests assert the real unbounded-space sampling error instead of a panic; RRTConnect's test goal sampler succeeds so initialization does not mask uniform-sampling failure. Test additions remain uncommitted. This closes only the failure-path task and its unsampleable-space acceptance criterion; PRM partial-roadmap cleanup, Python/JS error-path checks, and the full test matrix remain pending. Story status and release gates are unchanged.

PRM cleanup validation — 2026-10-05

Owner implemented explicit sampling-error handling in PRM::construct_roadmap(): clear the partial roadmap and return Err(e.into()), leaving the advanced RNG stored in the planner. The module-local test_partial_roadmap accepts one milestone, fails the next sample with ZeroVolume, and asserts the exact planning error plus an empty roadmap. It failed before the fix with one retained milestone and now passes in 0.00s alongside the existing immediate-failure PRM test (2 passed). All five public-boundary seeded-RNG continuity guards also pass; formatting, core all-targets clippy -D warnings, and git diff --check pass. The cleanup prevents the nonempty-roadmap early-return on retry; the current partial-roadmap test does not yet make an explicit second construction call. Changes are staged in improve-stabilty-robustness, not committed. Checked off only the PRM cleanup task; bindings error-path checks and the full test matrix remain unverified. S1.2 stays in-progress and continues to gate S1.5 and E1.

Python bindings validation — 2026-10-05

B1–B5 verified in improve-stabilty-robustness. The owner's initial just build-py log compiled junior-learning; this was corrected by rebuilding with just build-py in the intended worktree, which successfully installed the current core's extension into that worktree's Python 3.13 virtual environment. Import verification uses the same .venv. Owner completed the four assertions in oxmpl-py/tests/test_sampling_errors.py: PRM construction and RRT/RRTStar/RRTConnect solve calls raise normal Python exceptions matching dimension 0 is unbounded. Tree goal bias is 0.0; the fixed goal sampler succeeds for RRTConnect goal-root initialization. .venv/bin/python -m pytest oxmpl-py/tests/test_sampling_errors.py -q: 4 passed in 0.01s, including a fresh repeat at PM check-off. The new test file remains untracked/uncommitted. Only B1–B5 are checked off; no Python production binding edits were needed. B6–B11, the combined bindings acceptance criterion/parent task, and the full matrix remain open. S1.2 stays in-progress and continues to gate S1.5 and E1; no scope added or swapped.

JS bindings validation and B11 documentation check — 2026-10-05

B6 was already checked in TaskNotes when this PM update began. The senior-engineer verification rebuilt WASM with npm test -- tests/test_sampling_errors.test.js in improve-stabilty-robustness: all four JS tests passed against the rebuilt package. A fresh focused Vitest run passes all four again (5ms test execution), and ESLint plus Prettier checks now pass. PRM construction and RRT/RRTStar/RRTConnect solve throw strings matching Sampling.*dimension 0 is unbounded; the fixed goal callback lets RRTConnect initialize successfully first. Checked off B7–B10 and the combined Python/JS error-surfacing acceptance criterion, with B1–B10 now complete. No production binding changes were needed.

Inspected both binding READMEs: each contains only the project introduction, documentation link, and license, with no planner-error guidance. B11 remains open and now names the two missing notes explicitly: Python Exception/message handling and JS thrown-string handling for tree solve and PRM roadmap construction. The bindings parent task remains open until this documentation child is complete. The full matrix remains unverified, so S1.2 stays in-progress and release dependencies remain unchanged. Worktree was clean at this verification. No scope added or swapped.

B11 mdBook documentation — 2026-10-05

Owner clarified that user documentation belongs in the mdBook under docs/ and approved two short API notes after inspection. The book's included Python/JS examples already demonstrate catching planner failures; the API references lacked the explicit exception/thrown-value contract. Added a Planner errors paragraph to docs/src/python_api.md and docs/src/js_api.md, covering solve and PRM construction. Python guidance uses except Exception as e / str(e); JavaScript guidance reads the caught string directly rather than .message. This supersedes the earlier README location recommendation; no duplicate README guidance or new chapter was added. just docs and git diff --check pass in improve-stabilty-robustness. Checked off B11 and the bindings parent task. S1.2 remains in-progress; the full test matrix and final clippy gate remain open. No scope added or swapped; release gates are unchanged.

Deferred robust binding errors — 2026-10-05

Owner requested robust binding error/exception handling as future work rather than a current-sprint API change, and asked to replace the current-behavior documentation paragraphs with NOTE/TODO callouts. Both mdBook API pages now carry that reminder. Goal: distinguish planner failure kinds without message parsing; Python structured exceptions and JavaScript proper Error objects. No exception hierarchy, mapping, target release, or sprint has been selected. Recorded the deliberate deferral, reason, and revisit condition in the vault backlog parking lot; revisit at release planning after 0.7.0. This supersedes B11's earlier documentation wording. B11 and the bindings parent task remain complete under the owner's revised deliverable; no typed-error implementation is claimed. just docs and git diff --check pass. S1.2 remains in-progress with the full matrix and final clippy gate open; no current-sprint implementation scope added or swapped.

Final matrix validation — 2026-10-05

Ran just build-py then just test-all in improve-stabilty-robustness at HEAD 04f8755 (clean worktree). Python was rebuilt into the worktree's virtual environment; the JS test recipe rebuilt WASM before Vitest. All passed: Rust 91 checks (44 unit tests, 32 integration tests, 15 doc-tests), Python 34 tests in 30.33s, and JS/WASM 30 tests across 26 files (3.58s Vitest duration). cargo fmt --all -- --check, cargo clippy -p oxmpl --all-targets -- -D warnings, and git diff --check also pass. The mdBook build passed earlier for the final NOTE/TODO callouts. Validation log: /tmp/oxmpl-full-matrix.aHMOT5.log (machine-local, temporary). Checked off the final acceptance criterion and full-matrix task; all story checklists are complete, so S1.2 is done. Robust structured binding errors remain explicitly deferred; no new API work is claimed. Release/publishing work in S1.5 has not been run.