Skip to content

Control card routing for managing subscription for decalarative tasks#2817

Open
mustafab0 wants to merge 5 commits into
mustafa/control-coordinator-refactor-mainfrom
control-card-routing
Open

Control card routing for managing subscription for decalarative tasks#2817
mustafab0 wants to merge 5 commits into
mustafa/control-coordinator-refactor-mainfrom
control-card-routing

Conversation

@mustafab0

Copy link
Copy Markdown
Contributor

Problem

Adding routing and dispatcher for control coordinator topics

Contributor License Agreement

  • I have read and approved the CLA.

mustafab0 added 2 commits July 8, 2026 09:48
Pins the wire-visible routing semantics of all five coordinator input
streams (joint_command position/velocity precedence, frame_id-addressed
cartesian and EEF twist, teleop buttons, the twist->virtual-joint BASE
mapping and set_velocity_command fan-out, and subscription lifecycle)
ahead of the card-routing refactor. Green against the pre-refactor
coordinator; must stay green unchanged after it.
…ards

The coordinator no longer knows task types by name for four of its five
input streams. Tasks registered with a task_type get the stream routes
their card declares (claim_overlap / by_task_name / broadcast), one
generic _dispatch replaces the four per-stream handlers, and streams
subscribe when their route table is non-empty. eef_twist gets its card
and coordinator_ee_twist_command becomes card-routable.

twist_command deliberately keeps its legacy path (BASE virtual-joint
mapping + set_velocity_command fan-out, same subscription gate); only
its internal joint fan-in now goes through _dispatch. A follow-up PR
migrates it.

Intentional deltas: teleop buttons narrow from all-tasks to card-bound
tasks (only teleop_ik overrides on_buttons in-tree), and a task added
via bare add_task() no longer receives stream routing — pass task_type
to bind its card. Per-task handler delivery is now guarded by
try/except instead of unwinding the dispatch loop.

Characterization tests from the previous commit pass unchanged.
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.69053% with 10 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/control/test_coordinator_routing.py 97.89% 7 Missing ⚠️
dimos/control/coordinator.py 96.25% 1 Missing and 2 partials ⚠️
@@                             Coverage Diff                              @@
##             mustafa/control-coordinator-refactor-main    #2817   +/-   ##
============================================================================
  Coverage                                             ?   72.41%           
============================================================================
  Files                                                ?      946           
  Lines                                                ?    84841           
  Branches                                             ?     7694           
============================================================================
  Hits                                                 ?    61434           
  Misses                                               ?    21341           
  Partials                                             ?     2066           
Flag Coverage Δ
OS-ubuntu-24.04-arm 64.86% <97.69%> (?)
OS-ubuntu-latest 67.41% <97.69%> (?)
Py-3.10 67.39% <97.69%> (?)
Py-3.11 67.39% <97.69%> (?)
Py-3.12 67.39% <97.69%> (?)
Py-3.13 67.40% <97.69%> (?)
Py-3.14 67.41% <97.69%> (?)
Py-3.14t 67.40% <97.69%> (?)
SelfHosted-Large 30.22% <23.09%> (?)
SelfHosted-Linux 37.95% <23.09%> (?)
SelfHosted-macOS 36.28% <23.09%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/control/routing.py 100.00% <100.00%> (ø)
dimos/control/tasks/eef_twist_task/_registry.py 100.00% <100.00%> (ø)
dimos/control/test_control.py 94.60% <100.00%> (ø)
dimos/control/coordinator.py 66.58% <96.25%> (ø)
dimos/control/test_coordinator_routing.py 97.89% <97.89%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires up the declarative card-routing system (introduced in the prior refactor) so the coordinator actually uses it. Per-stream subscriptions for joint_command, coordinator_cartesian_command, and coordinator_ee_twist_command are replaced by a dynamic route table built at task-registration time from each task type's TASK_CONSUMES card.

  • add_task now accepts an optional task_type that looks up the card registry and populates _routes; _sync_stream_subscriptions subscribes streams when the first consumer registers and unsubscribes when the last consumer leaves, resolving the stale-subscription issue flagged in the previous review.
  • _dispatch handles three routing modes (CLAIM_OVERLAP, BY_TASK_NAME, BROADCAST) with per-task exception isolation, replacing the three ad-hoc _on_* callbacks.
  • The eef_twist task card (TASK_CONSUMES) is added in this PR; teleop_ik and cartesian_ik cards already existed and are now active.

Confidence Score: 5/5

The change is safe to merge. All three imperative subscription paths are cleanly replaced by the declarative route table, subscription lifecycle is correctly implemented, and handler-exception isolation prevents one failing task from blocking others.

The refactor ships a thorough characterization test suite covering normal delivery, unmatched routing, subscription lifecycle, error isolation, and runtime card registration. No regressions in existing behavior were identified.

No files require special attention. The two observations in coordinator.py are non-blocking: the stale snapshot race has a harmless worst-case outcome, and the BROADCAST+BY_TASK_NAME warning edge case cannot be triggered by any currently registered card.

Important Files Changed

Filename Overview
dimos/control/coordinator.py Core refactor: replaces three hardcoded on* subscription paths with a generic _dispatch/route table; adds _register_routes and _sync_stream_subscriptions; addresses all prior review concerns.
dimos/control/routing.py Adds coordinator_ee_twist_command to CONSUMABLE_STREAMS and updates the module docstring to reflect that the coordinator now actively uses these bindings.
dimos/control/tasks/eef_twist_task/_registry.py Adds TASK_CONSUMES card binding coordinator_ee_twist_command to on_ee_twist_command via BY_TASK_NAME routing, completing the eef_twist migration to declarative routing.
dimos/control/test_coordinator_routing.py New comprehensive characterization test suite for all five input streams; good coverage of routing modes, subscription lifecycle, error isolation, and runtime card registration.
dimos/control/test_control.py Existing tests updated to use RecordingTask and _dispatch seam instead of the removed _on_ee_twist_command and _stream_unsubs internals; correctly mirrors the new coordinator contract.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[add_task task, task_type] --> B{task_type provided?}
    B -- Yes --> C[_register_routes\nlookup card bindings]
    B -- No --> D[task registered\nno stream routing]
    C --> E{bindings.consumes\nempty?}
    E -- Yes + unknown type --> F[logger.warning\nunknown task_type]
    E -- No --> G[populate _routes\nstream to task, handler, routing]
    G --> H[_sync_stream_subscriptions]
    D --> H
    F --> H
    H --> I{tick_loop running?}
    I -- No --> J[no-op, deferred to start]
    I -- Yes --> K{new active streams?}
    K -- Yes --> L[subscribe stream]
    L --> M[_stream_unsubs stream = unsub]
    K -- No lost consumers? --> N[pop and call unsub]
    M --> O[Incoming message on stream]
    N --> O
    O --> P[_dispatch stream, msg]
    P --> Q{routing mode}
    Q -- CLAIM_OVERLAP --> R{joints overlap?}
    R -- Yes --> S[call handler]
    R -- No --> T[skip task]
    Q -- BY_TASK_NAME --> U{frame_id matches?}
    U -- Yes --> V[call handler]
    U -- No --> W[skip, warn if none matched]
    Q -- BROADCAST --> S
    S --> X{exception?}
    X -- Yes --> Y[log, continue]
    X -- No --> Z[done]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[add_task task, task_type] --> B{task_type provided?}
    B -- Yes --> C[_register_routes\nlookup card bindings]
    B -- No --> D[task registered\nno stream routing]
    C --> E{bindings.consumes\nempty?}
    E -- Yes + unknown type --> F[logger.warning\nunknown task_type]
    E -- No --> G[populate _routes\nstream to task, handler, routing]
    G --> H[_sync_stream_subscriptions]
    D --> H
    F --> H
    H --> I{tick_loop running?}
    I -- No --> J[no-op, deferred to start]
    I -- Yes --> K{new active streams?}
    K -- Yes --> L[subscribe stream]
    L --> M[_stream_unsubs stream = unsub]
    K -- No lost consumers? --> N[pop and call unsub]
    M --> O[Incoming message on stream]
    N --> O
    O --> P[_dispatch stream, msg]
    P --> Q{routing mode}
    Q -- CLAIM_OVERLAP --> R{joints overlap?}
    R -- Yes --> S[call handler]
    R -- No --> T[skip task]
    Q -- BY_TASK_NAME --> U{frame_id matches?}
    U -- Yes --> V[call handler]
    U -- No --> W[skip, warn if none matched]
    Q -- BROADCAST --> S
    S --> X{exception?}
    X -- Yes --> Y[log, continue]
    X -- No --> Z[done]
Loading

Reviews (2): Last reviewed commit: "style(control): trim added docstrings to..." | Re-trigger Greptile

Comment thread dimos/control/coordinator.py Outdated
Comment thread dimos/control/coordinator.py
Comment thread dimos/control/coordinator.py
mustafab0 added 3 commits July 8, 2026 15:50
…umer removal

Addresses review feedback on the card-routing PR:
- CLAIM_OVERLAP gate + empty-name early-return were only exercised through
  real servo/velocity tasks, which self-filter and mask over-delivery. Add a
  pure-recorder ProbeTask test that pins the dispatcher's own overlap gate.
- The per-task try/except in _dispatch had no test; add one where a raising
  handler must not abort delivery to a sibling on the same stream nor
  propagate out of the port callback.
- remove_task pruned routes but left the subscription live when the last
  consumer went away. _sync_stream_subscriptions now reconciles both
  directions (subscribe active streams, drop empty ones) and remove_task
  calls it; add a test that removing the last consumer unsubscribes the stream.

All three gaps were mutation-verified: each new test fails when its target
code is neutered.
A task_type with no registered card silently set up no routing, so a typo
was indistinguishable from a legitimately card-less type. _register_routes
now warns when the type is unknown to the registry entirely (no factory and
no card), while staying quiet for real card-less types like trajectory. The
config path already fails loudly at registry.create(); this covers the
direct add_task(task, task_type=...) RPC path.

Tests pin both the warning and the no-false-positive-on-trajectory
behavior; both mutation-verified.
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant