Skip to content

feat: dimos spy - universal transport spy over LCM + Zenoh#2735

Open
leshy wants to merge 15 commits into
mainfrom
feat/ivan/spy
Open

feat: dimos spy - universal transport spy over LCM + Zenoh#2735
leshy wants to merge 15 commits into
mainfrom
feat/ivan/spy

Conversation

@leshy

@leshy leshy commented Jul 6, 2026

Copy link
Copy Markdown
Member

replaces lcmspy

2026-07-06_19-39

Design ratified in ticket f6c74d39 (subscribe_all non-conflating decision
confirmed by Ivan). Task package for a worker to implement:

- TASK.md: goal, decisions, implementation map, acceptance
- docs/usage/transports/spy.md: design doc (why, architecture, decisions)
- spec.py: subscribe_all contract tightened (every message, no conflation);
  SubscribeLatestMixin.subscribe_latest() stub = the explicit conflation opt-in
- protocol/pubsub/spy.py: TopicStats / SpySource / LCMSpySource /
  ZenohSpySource / TransportSpy stubs (typed, NotImplementedError bodies)
- utils/cli/spy/run_spy.py + `dimos spy` CLI registration (TUI stub)
- test_spy.py: 17 contract tests, 15 failing until implemented; the zenoh
  subscribe_all conflation bug fails sharply (2 of 50 messages delivered)

mypy clean (796 files); existing pubsub suites green.
Implement the spy core (TopicStats, LCM/Zenoh SpySources, TransportSpy) that
taps the raw-bytes pubsub layer — per-message hot path is (topic, len, ts),
never decoding a payload — plus the Textual TUI (dimos spy) and CLI wiring.

Spec fix: subscribe_all is now non-conflating on every transport; conflation
moves to the opt-in SubscribeLatestMixin.subscribe_latest() (lifted from zenoh,
transport-agnostic). Zenoh subscribe_all widens dimos/** -> ** and keeps its
drain-stop bookkeeping via _register/_unregister_drain_stop hooks. Rerun bridge
migrates to subscribe_latest. dimos lcmspy becomes a deprecated alias for
dimos spy --transport lcm.

Also strip 14 decorative section-banner comments from the delivered test_spy.py
to satisfy codebase_checks/test_no_sections (approved by brain; no logic change).
@mintlify

mintlify Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
dimensional 🟢 Ready View Preview Jul 6, 2026, 4:36 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

- TransportSpy.start() rolls back already-started sources on failure
- subscribe_latest stops+joins the drain thread if subscribe_all raises
- SOURCE_FACTORIES registry: construct only the requested transports (a
  filtered-out transport is never imported or instantiated)
- run_spy/SpyApp validate unknown --transport names up front
- add contract tests for the failure-rollback paths and run_spy selection
@leshy leshy changed the title feat: dimos spy — universal transport spy over LCM + Zenoh feat: dimos spy - universal transport spy over LCM + Zenoh Jul 6, 2026
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the old LCM spy with a universal transport spy.

  • Adds dimos spy for LCM and Zenoh traffic.
  • Keeps lcmspy as an LCM-only alias.
  • Adds transport filtering and clear argument validation.
  • Degrades the default spy when one backend is unavailable.
  • Updates docs, tests, and the console entrypoint.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
dimos/utils/cli/spy/core.py Adds the transport-agnostic spy core, backend source handling, best-effort default startup, and topic statistics.
dimos/utils/cli/spy/run_spy.py Adds the TUI entrypoint, transport parsing, explicit transport startup, and the LCM-only console shim.
dimos/robot/cli/dimos.py Adds the dimos spy command and routes dimos lcmspy through the shared LCM-only helper.
pyproject.toml Updates the standalone lcmspy console script to use the new LCM-only spy shim.

Reviews (10): Last reviewed commit: "refactor: move spy core out of protocol/..." | Re-trigger Greptile

Comment thread dimos/protocol/pubsub/spy.py Outdated
Comment thread dimos/utils/cli/spy/run_spy.py
- dimos lcmspy subcommand: drop deprecation/guard, just run spy --transport lcm
- lcmspy console-script repointed to run_spy:lcm_main shim (LCM-only spy)
- delete the superseded dimos/utils/cli/lcmspy/ module (TUI + stats + tests)
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
dimos/utils/cli/spy/run_spy.py 64.42% 35 Missing and 2 partials ⚠️
dimos/utils/cli/spy/test_spy.py 98.85% 2 Missing and 1 partial ⚠️
dimos/utils/cli/spy/core.py 99.31% 1 Missing ⚠️
dimos/utils/cli/spy/test_run_spy.py 99.13% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main    #2735      +/-   ##
==========================================
- Coverage   72.35%   72.33%   -0.03%     
==========================================
  Files         902      907       +5     
  Lines       81820    82526     +706     
  Branches     7396     7430      +34     
==========================================
+ Hits        59201    59693     +492     
- Misses      20639    20850     +211     
- Partials     1980     1983       +3     
Flag Coverage Δ
OS-ubuntu-24.04-arm 64.71% <93.64%> (+0.17%) ⬆️
OS-ubuntu-latest 67.34% <93.64%> (+0.15%) ⬆️
Py-3.10 67.33% <93.64%> (+0.15%) ⬆️
Py-3.11 67.32% <93.64%> (+0.15%) ⬆️
Py-3.12 67.33% <93.64%> (+0.15%) ⬆️
Py-3.13 67.33% <93.64%> (+0.15%) ⬆️
Py-3.14 67.34% <93.64%> (+0.15%) ⬆️
Py-3.14t 67.32% <93.64%> (+0.14%) ⬆️
SelfHosted-Linux 38.01% <25.26%> (-0.03%) ⬇️
SelfHosted-macOS 36.35% <25.26%> (-0.02%) ⬇️

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

Files with missing lines Coverage Δ
dimos/robot/cli/dimos.py 64.61% <100.00%> (+1.56%) ⬆️
dimos/robot/cli/test_dimos.py 100.00% <100.00%> (ø)
dimos/utils/cli/spy/core.py 99.31% <99.31%> (ø)
dimos/utils/cli/spy/test_run_spy.py 99.13% <99.13%> (ø)
dimos/utils/cli/spy/test_spy.py 98.85% <98.85%> (ø)
dimos/utils/cli/spy/run_spy.py 64.42% <64.42%> (ø)

... and 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- default_sources() skips backends that fail to import with a one-line
  stderr warning and errors only if no transport is available; an
  explicitly requested --transport keeps the hard error
- spy web mode serves the child with sys.executable and forwards the
  --transport filter args (validated before serving)
- restore the lcmspy --transport override guard dropped by the thin-alias
  refactor
- tests: default degradation, explicit hard error, web command construction
Comment thread dimos/robot/cli/dimos.py Outdated
dimos lcmspy web previously became spy --transport lcm web, which missed
run_spy.main()'s web branch (web must be argv[0]) and silently launched
the local TUI. Keep web first and forward the LCM filter behind it; the
--transport override guard stays. Alias routing is now covered by
CliRunner tests (tui, web, override rejection).
Reverses the ratified subscribe_all non-conflation spec change (Ivan's call):
- zenoh subscribe_all: restore original latest-per-topic drain over dimos/**
- spec: drop SubscribeLatestMixin/subscribe_latest entirely
- rerun bridge: back to subscribe_all (its original conflating consumer)
- spy is transport-agnostic — it just calls bus.subscribe_all(), accepting
  whatever delivery semantics each transport provides (LCM: every message;
  zenoh: latest-per-topic). No raw/session-level special-casing.
- drop the now-invalid contract tests: subscribe_all-delivers-every[zenoh]
  and subscribe_latest[*]; docstrings updated to match.

Keeps the three Greptile fixes already on feat/ivan/spy (default_sources
degradation, web-mode filter forwarding, lcmspy-web routing).
Comment thread dimos/utils/cli/spy/run_spy.py Outdated
Per maintainer decision, drop the browser 'web' mode entirely:
- run_spy: remove the web branch + _web_command; main() just runs the TUI
- docs: drop the 'dimos spy web' line

Fix the standalone lcmspy console entry (Greptile P1): it discarded
sys.argv[1:]. Extract _lcm_only_argv() — shared by lcm_main() and the
dimos lcmspy alias — which forwards args to spy and rejects an explicit
--transport override (LCM-only). Tests for forwarding + rejection on both
entry points; drop the now-obsolete web tests.
Comment thread dimos/utils/cli/spy/run_spy.py
Comment thread dimos/protocol/pubsub/spy.py Outdated
…rror

Two Greptile P1s on PR #2735:
- run_spy._parse_transports now rejects unexpected positionals/flags (and a
  --transport with no value) instead of silently ignoring them, so an
  unrecognized argument fails loudly rather than quietly launching the TUI.
- default_sources() catches any construction failure (native init, ctor
  error), not just ImportError: warn + skip on the default path, error only
  if nothing constructs. Explicit --transport still hard-fails.

Tests: stray-positional rejection (unit + CLI for spy and lcmspy),
--transport missing-value, and a non-ImportError backend-skip case.
Comment thread dimos/utils/cli/spy/run_spy.py
Greptile P1 on PR #2735: default `dimos spy` could construct both sources then
die if one failed during start()/tap(), because TransportSpy.start() always
rolled everything back and re-raised.

Add a best_effort mode to TransportSpy.start(): a source that fails to
start/tap is stopped, warned, and skipped, leaving the survivors running;
it raises only if no source starts at all. Track live sources so stop() only
touches ones that actually started. SpyApp uses best_effort on the default
(no --transport) path; an explicit --transport keeps strict all-or-nothing
rollback. Ctrl-C/SystemExit still roll back and propagate in both modes.

Tests: best-effort skip-with-survivor + all-fail-raises at the TransportSpy
level, and SpyApp default-degrades vs explicit-hard-fails wiring.
leshdaemon and others added 2 commits July 6, 2026 20:36
The standalone transports/spy.md is now a few lines, so merge it into the
Transports doc's CLI section (retitled 'Inspecting traffic') and drop the
page from the nav. Update the spy.py module docstring to point at the
transports doc instead of the removed page.
The spy core (TopicStats, SpySource, LCM/Zenoh sources, TransportSpy) is an
observability tool built on top of the pubsub layer, not part of it — it only
depends on the pubsub impls, and its only consumers are the spy CLI and tests.
Sitting in protocol/pubsub/ was a layering inversion. Move it next to run_spy:

  dimos/protocol/pubsub/spy.py       -> dimos/utils/cli/spy/core.py
  dimos/protocol/pubsub/test_spy.py  -> dimos/utils/cli/spy/test_spy.py

Imports updated; no behavior change. pubsub suite and spy tests green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants