fix(control): arm streamed path before task is active#3159
Conversation
Greptile SummaryThe PR allows streamed paths to activate an idle holonomic pose follower.
Confidence Score: 3/5The PR should not merge until cancellation and preemption reliably discard pending paths instead of allowing them to start later. A pending path now independently keeps the task schedulable, while cancellation and preemption only change the state; compute can therefore arm and execute work that was already cancelled, or leave the task active indefinitely without pose data. dimos/control/tasks/holonomic_pose_follower_task/holonomic_pose_follower_task.py Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
P[Path stream] --> L[Latch pending path]
L --> A[is_active returns true]
A --> C[Tick loop calls compute]
C --> Q{Pose available?}
Q -->|Yes| S[start_path]
Q -->|No| W[Keep path pending]
W --> A
X[Cancel or preempt] --> B[Set state aborted]
B --> A
Reviews (1): Last reviewed commit: "fix(control): arm streamed path before t..." | Re-trigger Greptile |
| @@ -150,7 +150,7 @@ def claim(self) -> ResourceClaim: | |||
| ) | |||
|
|
|||
| def is_active(self) -> bool: | |||
There was a problem hiding this comment.
Cancellation leaves pending path active
When a path is cancelled or preempted before pose data is available, _pending_path remains set, so this condition keeps scheduling the task and later arms the cancelled path once pose data arrives, causing the robot to execute work that was cancelled. Without pose data, the task remains active indefinitely and can block removal of hardware whose joints it claims.
Knowledge Base Used: Control Coordinator and Control Tasks
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
Summary
HolonomicPoseFollowerTasknever starts driving a path that arrives via thepathstream card (on_path). The robot receives the path and logs it, but doesn't move.Root cause
compute()is the only thing that arms a pending path:on_path()just latches it (self._pending_path = msg), andcompute()arms it on the next tick that has a pose, by callingstart_path().But the tick loop only calls
compute()on tasks whereis_active()is alreadyTrue:And
is_active()only checked states thatstart_path()itself sets:The task starts in
"idle", sois_active()isFalseuntilcompute()runs, butcompute()never runs untilis_active()isTrue. It's a permanent deadlock with no error, since nothing throws; the task just never starts.Fix
is_active()also returnsTruewhile a path is pending arm, so the tick loop letscompute()run once to arm it. After that, the normal state-based check takes over.Test plan