From e0d1cec8449b51b7ea1148ce7cec2b3fb8f4d169 Mon Sep 17 00:00:00 2001 From: jepson2k <55201008+Jepson2k@users.noreply.github.com> Date: Mon, 27 Jul 2026 13:40:32 -0400 Subject: [PATCH 1/5] Gate trajectory settling on progress, not elapsed ticks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The settle phase completed a segment after a fixed 20-tick cap even with the firmware still closing on the target, and the mock transport freezes all residual motion once the command stream goes idle — on a starved host the simulated plant falls behind the waypoint stream, the cap fires, and the robot is reported complete while stranded short of the target (waldo-commander's hand-eye CI observed home "complete" with J1 29 deg from standby). Settling now resets the tick counter whenever the position error shrinks, so the cap only fires after 20 ticks without progress — preserving the anti-hang escape for real hardware's steady-state residual — and an unconverged completion logs the residual instead of passing silently. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EdLG1uiKKZJ6aAeiojeRwd --- parol6/server/segment_player.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/parol6/server/segment_player.py b/parol6/server/segment_player.py index 3eb9ef5..080e2a2 100644 --- a/parol6/server/segment_player.py +++ b/parol6/server/segment_player.py @@ -17,7 +17,6 @@ from typing import TYPE_CHECKING import numpy as np -from pinokin import arrays_equal_n from parol6.commands._collision_guard import guard_joint_path from parol6.commands.base import CommandBase, ExecutionStatusCode @@ -60,6 +59,7 @@ class SegmentPlayer: "_inline_activated", "_settling", "_settle_ticks", + "_settle_err", "_last_shapes_version", ) @@ -72,6 +72,7 @@ def __init__(self, planner: MotionPlanner) -> None: self._inline_activated: bool = False self._settling: bool = False self._settle_ticks: int = 0 + self._settle_err: int = -1 self._last_shapes_version: int = 0 @property @@ -127,16 +128,36 @@ def tick(self, state: ControllerState) -> bool: self._step += 1 self._settling = False return True - # All waypoints sent — hold MOVE at target until Position_in converges + # All waypoints sent — hold MOVE at target until Position_in + # converges. The tick cap gates on stall, not elapsed time: + # while the firmware is still closing on the target (e.g. it + # fell behind the waypoint stream under CPU starvation) the + # segment stays active, so completion is never reported with + # the robot still in motion. target = active.trajectory_steps[-1] if not self._settling: self._settling = True self._settle_ticks = 0 + self._settle_err = -1 + err = 0 + for i in range(6): + d = int(state.Position_in[i]) - int(target[i]) + if d < 0: + d = -d + if d > err: + err = d + if self._settle_err < 0 or err < self._settle_err: + self._settle_err = err + self._settle_ticks = 0 self._settle_ticks += 1 - if ( - arrays_equal_n(state.Position_in[:6], target[:6]) - or self._settle_ticks > SETTLE_MAX_TICKS - ): + if err == 0 or self._settle_ticks > SETTLE_MAX_TICKS: + if err != 0: + logger.warning( + "Segment completed %d steps short of target " + "(no settle progress for %d ticks)", + err, + SETTLE_MAX_TICKS, + ) self._settling = False self._complete_segment(active, state) continue From 9fd7185699389c1fe472db8629d7ae8a9916c777 Mon Sep 17 00:00:00 2001 From: jepson2k <55201008+Jepson2k@users.noreply.github.com> Date: Thu, 6 Aug 2026 14:52:28 -0400 Subject: [PATCH 2/5] Annotate the clamped-spline bc assignment so ty keeps it Any scipy-stubs 1.18 types bc_type derivative values as array-likes only, while scipy requires scalars for 1-D y. ty narrows a bare-declared Any on assignment, so the tuple branch failed overload resolution; an annotated assignment keeps bc at its declared Any. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LkVPPMQg33tFuGCVLkyrJQ --- parol6/motion/geometry.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/parol6/motion/geometry.py b/parol6/motion/geometry.py index e36cb0b..b581a79 100644 --- a/parol6/motion/geometry.py +++ b/parol6/motion/geometry.py @@ -192,9 +192,10 @@ def generate_spline( pos_splines = [] for i in range(3): - bc: Any + # Annotated assignment keeps bc as Any: scipy-stubs' bc_type rejects + # the scalar derivative values scipy requires for 1-D y if velocity_start is not None and velocity_end is not None: - bc = ((1, float(velocity_start[i])), (1, float(velocity_end[i]))) + bc: Any = ((1, float(velocity_start[i])), (1, float(velocity_end[i]))) else: bc = "not-a-knot" spline = CubicSpline(timestamps_arr, waypoints_arr[:, i], bc_type=bc) From 514490a6ad211fe42bbc7c3280fc463729b2eeb8 Mon Sep 17 00:00:00 2001 From: jepson2k <55201008+Jepson2k@users.noreply.github.com> Date: Sat, 8 Aug 2026 03:30:09 -0400 Subject: [PATCH 3/5] Drop a stale ty ignore code and a redundant memoryview cast ty 0.0.69 resolves ndarray.tolist() without the ty-specific suppression and infers the frame memoryview union directly; it now warns on both leftovers, and warnings fail the lint hook. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LkVPPMQg33tFuGCVLkyrJQ --- parol6/protocol/wire.py | 2 +- parol6/server/transports/serial_transport.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/parol6/protocol/wire.py b/parol6/protocol/wire.py index 231d7e0..204f90f 100644 --- a/parol6/protocol/wire.py +++ b/parol6/protocol/wire.py @@ -43,7 +43,7 @@ def _enc_hook(obj: object) -> object: """Custom encoder hook for numpy types.""" if isinstance(obj, np.ndarray): - return obj.tolist() # type: ignore[no-matching-overload, ty:no-matching-overload] + return obj.tolist() # type: ignore[no-matching-overload] if isinstance(obj, (np.integer, np.floating)): return obj.item() raise NotImplementedError(f"Cannot encode {type(obj)}") diff --git a/parol6/server/transports/serial_transport.py b/parol6/server/transports/serial_transport.py index ace4b03..a26e822 100644 --- a/parol6/server/transports/serial_transport.py +++ b/parol6/server/transports/serial_transport.py @@ -8,7 +8,6 @@ import logging import os import time -from typing import cast import numba import numpy as np @@ -415,9 +414,7 @@ def get_latest_frame_view(self) -> tuple[memoryview | None, int, float]: Return a tuple of (memoryview|None, version:int, timestamp:float). The memoryview points to a stable 52-byte buffer which is updated by the reader. """ - mv = cast( - "memoryview | None", self._frame_mv if self._frame_version > 0 else None - ) + mv = self._frame_mv if self._frame_version > 0 else None return (mv, self._frame_version, self._frame_ts) def _update_hz_tracking(self) -> None: From 4869fd157dc7f6a706f491af53c9ebb03e1bd8a7 Mon Sep 17 00:00:00 2001 From: jepson2k <55201008+Jepson2k@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:47:51 -0400 Subject: [PATCH 4/5] Pin scipy-stubs in the dev extra Unpinned stub releases have broken the ty lint hook twice; ty itself stays unpinned deliberately while it is still in beta. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LkVPPMQg33tFuGCVLkyrJQ --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4a08877..b3e14fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ dev = [ "trimesh", "fast-simplification", "rtree", - "scipy-stubs", + "scipy-stubs==1.18.0.1", "types-pyserial", ] From e2de8be2acee0751c04d48c4b94ab92fe031f564 Mon Sep 17 00:00:00 2001 From: jepson2k <55201008+Jepson2k@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:50:03 -0400 Subject: [PATCH 5/5] Split the scipy-stubs pin by Python version scipy-stubs 1.18.x requires Python >=3.12, so a single exact pin is uninstallable on the 3.11 CI jobs; 3.11 pins the last 1.17 release. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LkVPPMQg33tFuGCVLkyrJQ --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b3e14fa..78015cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,8 @@ dev = [ "trimesh", "fast-simplification", "rtree", - "scipy-stubs==1.18.0.1", + "scipy-stubs==1.17.1.5; python_version < '3.12'", + "scipy-stubs==1.18.0.1; python_version >= '3.12'", "types-pyserial", ]