`oxmpl` - sprint-005

Sprint 005 — Binding errors and resolution exposure (provisional)

Provisional scaffold for release 0.9.0 (E4), written 2026-10-08 so the release isn't lost. Nothing here is committed: the stories carry no oxmpl/sprint/005 tag until planning confirms them against the owner's stated availability. Dates are re-derived at the previous sprint's retro if it slipped.

Provisional commitment

Order: Either order. If S4.5 is done, S4.6's ConfigurationError can be wired to its "already in use" checks here.

Overlaps the year-end holidays — confirm availability at planning. Entry criteria: S4.2, S4.3 done (S4.5 recommended).

Blind pickup: how to (re)generate the tickets

Tickets for these stories already exist in TaskNotes (created 2026-10-08). Once a ticket exists, its TaskNotes details are authoritative and this page is only the regeneration spec. If a ticket is missing or was lost, recreate it from the spec below:

  1. tasknotes_create_task with the title, projects, tags and priority shown. MCP quirks: create drops the description and defaults scheduled to today, so follow with tasknotes_update_task to set details (the body below) and scheduled: null.
  2. Wire blockedBy by editing the task file's YAML frontmatter directly (no MCP tool exposes it). A story lists all upstream dependencies, including done ones.
  3. Verify with tasknotes_get_task: isBlocked: true on the story while its upstreams are open, and isBlocking on each upstream. After any later update_task, re-check that blockedBy survived.
  4. If the epic E4 is missing, recreate it first from the spec in oxmpl - sprint-003 § Epic E4.
  5. At this sprint's planning (owner confirms availability): add the oxmpl/sprint/NNN tag, set scheduled/due to the sprint dates, remove sprint-status: provisional from this page's frontmatter, and rewrite the "Provisional commitment" heading as "Commitment".

Conventions: oxmpl - planning. Repo context: ADR docs/planning/adr/0003-motion-validation.md (decisions), CONTEXT.md (glossary). 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.

Story specs

S4.6 — Binding error hierarchy (size-M)

# TaskNotes task — file: task_notes/tasks/S4.6 — Binding error hierarchy.md
title: "S4.6 — Binding error hierarchy"
status: open
priority: normal
projects: ["[[E4 Validation — 0.9.0]]"]
tags: [oxmpl, oxmpl/epic, oxmpl/story/size-M]   # add oxmpl/sprint/005 only when committed at planning
scheduled: null      # set to sprint start when committed
due: null            # set to sprint end when committed
blockedBy:           # wire in frontmatter by hand (no MCP tool exposes it)
  - uid: "[[E3 Toolchain refresh — 0.8.0]]"
    reltype: FINISHTOSTART
  - uid: "[[S4.2 — Fallible, thread-safe validation core]]"
    reltype: FINISHTOSTART

details body:

Intent

Python callers get a bare Exception with a message new_err(e.to_string()) — 25 sites across the four planners) and JS callers get thrown strings (map_err(|e| e.to_string()) — 30 sites. Give both bindings typed errors, and re-raise users' own exceptions unwrapped. Absorbs the backlog item "Robust binding errors" and resolves the TODO callouts at docs/src/python_api.md:129 and docs/src/js_api.md:179.

Decisions (ADR-0003; grilling Q16/Q18)

Acceptance criteria

Tasks

S4.8 — Resolution exposed in the bindings (size-M)

# TaskNotes task — file: task_notes/tasks/S4.8 — Resolution exposed in the bindings.md
title: "S4.8 — Resolution exposed in the bindings"
status: open
priority: normal
projects: ["[[E4 Validation — 0.9.0]]"]
tags: [oxmpl, oxmpl/epic, oxmpl/story/size-M]   # add oxmpl/sprint/005 only when committed at planning
scheduled: null      # set to sprint start when committed
due: null            # set to sprint end when committed
blockedBy:           # wire in frontmatter by hand (no MCP tool exposes it)
  - uid: "[[E3 Toolchain refresh — 0.8.0]]"
    reltype: FINISHTOSTART
  - uid: "[[S4.3 — Space resolution valid segment count]]"
    reltype: FINISHTOSTART

details body:

Intent

Only RealVector, SO2 and SO3 expose a resolution setter in either binding, so SE2/SE3/compound users cannot follow the 0.9.0 migration note ("set the fraction to 0.005"). Python exposes no interpolate or segment length at all. Expose resolution fully so users can tune spaces and so user motion validators (S4.7) can work at the space's resolution without re-implementing its maths.

Decisions (grilling Q15)

Current gaps (as of c558556)

Space Py setter Py length Py interpolate JS setter JS length JS interpolate
RealVector yes no no yes (old name) yes yes
SO2 yes no no yes (old name) yes yes
SO3 yes no no yes (old name) yes yes
SE2 no no no no yes yes
SE3 no no no no yes yes
Compound no no no no yes yes

Acceptance criteria

Notes

Cut candidates if the sprint runs short: interpolate and valid_segment_count (keep the setter — the migration note depends on it). Both can be added later without breaking anyone.

Progress log

Review & retro

At sprint end: done / carried over / dropped, what was over- or underestimated, and at most one process change for the next sprint.