topicforge 0.7.0__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {topicforge-0.7.0 → topicforge-0.7.1}/CHANGELOG.md +48 -1
- {topicforge-0.7.0 → topicforge-0.7.1}/PKG-INFO +6 -6
- {topicforge-0.7.0 → topicforge-0.7.1}/README.md +5 -5
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/CLIENTS.md +16 -16
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/CONTRACT.md +159 -13
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/DDS_QUICKSTART.md +1 -1
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/EVAL.md +14 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/TESTING.md +2 -2
- topicforge-0.7.1/docs/TOOLS.md +459 -0
- topicforge-0.7.1/docs/VALIDATION.md +44 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/dds-interop-matrix.md +2 -2
- {topicforge-0.7.0 → topicforge-0.7.1}/plugin/README.md +1 -1
- {topicforge-0.7.0 → topicforge-0.7.1}/plugin/pyproject.toml +2 -2
- {topicforge-0.7.0 → topicforge-0.7.1}/pyproject.toml +1 -1
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/__init__.py +1 -1
- topicforge-0.7.1/src/topicforge/adapters/common/qos_fixes.py +518 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_scan.py +50 -6
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/models/__init__.py +2 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/models/nodes.py +11 -21
- topicforge-0.7.1/src/topicforge/models/schemas.py +1269 -0
- topicforge-0.7.1/src/topicforge/models/summaries.py +242 -0
- topicforge-0.7.1/src/topicforge/services/dds_views.py +154 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/inspector.py +50 -9
- topicforge-0.7.1/src/topicforge/tools/handlers.py +699 -0
- topicforge-0.7.1/src/topicforge/tools/schema_compact.py +60 -0
- topicforge-0.7.1/tests/contract/analyze_bag.json +219 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/detect_qos_mismatches.json +54 -129
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/get_node_info.json +13 -40
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/get_topic_info.json +9 -30
- topicforge-0.7.1/tests/contract/health_check.json +303 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/list_endpoints.json +73 -128
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/list_nodes.json +4 -18
- topicforge-0.7.1/tests/contract/list_participants.json +298 -0
- topicforge-0.7.1/tests/contract/list_topics.json +96 -0
- topicforge-0.7.1/tests/contract/participant_events.json +239 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/peek_bag_samples.json +92 -209
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/peek_dds_samples.json +91 -207
- topicforge-0.7.1/tests/contract/sample_messages.json +1104 -0
- topicforge-0.7.1/tests/contract/topic_metrics.json +181 -0
- topicforge-0.7.1/tests/test_friction_fixes.py +315 -0
- topicforge-0.7.1/tests/test_qos_fixes.py +362 -0
- topicforge-0.7.0/docs/TOOLS.md +0 -452
- topicforge-0.7.0/docs/VALIDATION.md +0 -19
- topicforge-0.7.0/src/topicforge/models/schemas.py +0 -1555
- topicforge-0.7.0/src/topicforge/models/summaries.py +0 -328
- topicforge-0.7.0/src/topicforge/tools/handlers.py +0 -870
- topicforge-0.7.0/tests/contract/analyze_bag.json +0 -243
- topicforge-0.7.0/tests/contract/health_check.json +0 -332
- topicforge-0.7.0/tests/contract/list_participants.json +0 -293
- topicforge-0.7.0/tests/contract/list_topics.json +0 -117
- topicforge-0.7.0/tests/contract/participant_events.json +0 -216
- topicforge-0.7.0/tests/contract/sample_messages.json +0 -1223
- topicforge-0.7.0/tests/contract/topic_metrics.json +0 -201
- {topicforge-0.7.0 → topicforge-0.7.1}/.gitignore +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/LICENSE +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/MIGRATION-0.7.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/TROUBLESHOOTING.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/docs/TUTORIEL.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/00_hello_pub_sub/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/01_who_is_on_the_bus/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/02_why_cant_they_talk/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/03_a_node_crashed/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/04_late_joiner_misses_data/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/05_reliability_in_code/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/07_deadline_in_code/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/08_crash_seen_from_inside/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/10_lidar_silent_after_driver_swap/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/11_who_talks_to_whom/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/12_safety_monitor_dropout/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/13_deadline_not_offered/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/14_restart_loop/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/plugin/LICENSE +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/agent_eval/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/ground_truth/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/cyclone_c/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/dust_py/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/fast_py/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/rti_c/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/rti_cpp/README.md +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/__main__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/base.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/bag_kind.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/cdr_decoder.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/dds_helpers.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/dds_introspection.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/discovery_tracker.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/endpoints.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/lifecycle.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/nodes.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_analyzer.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_endpoints.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_normalize.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/ros_names.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/stamps.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/topic_filter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/xtypes.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/composite.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_cyclone/adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_fast/adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/echo_parser.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/echo_stream.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/node_parsers.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/parsers.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/process_runner.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/process_tree.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/sample_extras.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/fixtures.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/node_fixtures.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/budget.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/config/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/config/settings.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/constants.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/app.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/http.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/prompts.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/bag_service.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/bag_stats.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/factory.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/health.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/sample_budget.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/_common.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/laser_scan.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/motion.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/rate.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/sensor_data.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/telemetry/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/telemetry/client.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/tools/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/tools/annotations.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/tools/guard.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/conftest.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/_prompts.json +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ground_truth/omnisim_064_results.json +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ground_truth/omnisim_064_truth.json +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/image_noarr.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/image_trunc4.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_edge.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_noarr.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_trunc128.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_trunc3.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/string_latched.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/twist.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_info_humble.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_info_no_param_services.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_info_not_found.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_duplicates.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_empty.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_plain.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_warning_stdout.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/param_dump_humble.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/param_dump_secrets.yaml +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_clock.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_scan.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_tf.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/Dockerfile +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/__init__.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/blocked_node.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/entrypoint.sh +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/ground_truth.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/publisher.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/run_bench.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/test_live_adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/test_real_bus.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_analyze_bag_multi_format.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_omnisim_humble.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_service.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_stats.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_summaries.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_cdr_decoder.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_composite_adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_config.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_contract_shape.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_contract_snapshots.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_contract_v2.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_cyclone_adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_cross_vendor.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_helpers.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_inactive_reason.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_introspection.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_qos_normalization.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_schemas.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_discovery_tracker.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_echo_parser.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_echo_stream.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_endpoints.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_example_node_spec.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_factory.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_fast_adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_field_names.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_ground_truth_compare.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_health.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_history_and_hints.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_honest_outputs.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_http_transport.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_inspector.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_lifecycle_buffer.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_adapter_graph.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_adapter_parse.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_adapter_subprocess.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_sample_notes.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_metrics_buffer.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_mock_adapter.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_node_logic.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_node_parsers.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_node_tools.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_peek_bag_samples.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_process_runner.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_process_tree.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_prompts.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_qos_analyzer.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_qos_endpoints.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_qos_scan.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_rate_verdicts.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_sample_options.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_sample_summary_flow.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_server_guard.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_server_info_version.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_server_lanes.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_stamps.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_summaries.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_telemetry.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_tool_annotations.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_tool_descriptions.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_tools_integration.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_topic_metrics.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_version_consistency.py +0 -0
- {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_xtypes.py +0 -0
|
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.1] - 2026-10-14
|
|
11
|
+
|
|
12
|
+
QoS fix suggestions and agent-friction fixes from the blind evaluation (`docs/EVAL.md`). Additive only: no field or
|
|
13
|
+
input was renamed, removed or retyped (`docs/CONTRACT.md` section 9).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `detect_qos_mismatches` fills `suggested_fixes` (output contract 2, additive; see
|
|
18
|
+
`docs/CONTRACT.md` section 3.1): "diagnose, propose the exact fix, verify". Each QoS
|
|
19
|
+
mismatch report and each `partition` split gets up to one fix per side that can change
|
|
20
|
+
(`MismatchReport.suggested_fixes`, and the new `NotMatchedPair.suggested_fixes`), with its
|
|
21
|
+
tradeoff and a `verify_with` call. A ROS 2 topic with a Reliability or Durability finding
|
|
22
|
+
gets a `qos_override` parameter-file entry; every other case gets a `dds_profile` (Fast DDS
|
|
23
|
+
XML plus the cyclonedds-python equivalent, since Cyclone DDS has no XML QoS profile).
|
|
24
|
+
Covered: Reliability, Durability, Deadline, Liveliness, Ownership, LatencyBudget and
|
|
25
|
+
Partition; other findings get a hint instead. Identical findings on a topic share the
|
|
26
|
+
first report's fixes. Text only: TopicForge never applies a fix, and a test fails any
|
|
27
|
+
suggestion containing a stop, remove or write verb.
|
|
28
|
+
- `list_participants` and `participant_events`: `restarts_by_name` (`{name: {lives,
|
|
29
|
+
departures}}` for names seen with several GUIDs or lost) and `restarts_note`, so an agent
|
|
30
|
+
no longer counts GUIDs to find a restart loop.
|
|
31
|
+
- `participant_events`: `age_s` on each event, seconds between the event and the response
|
|
32
|
+
(`null` in mock mode).
|
|
33
|
+
- `detect_qos_mismatches`: `summary`, a one-line reading of the scan that says when pairs are
|
|
34
|
+
not matched and no data flows, even with an empty `reports`.
|
|
35
|
+
- `list_endpoints`: optional input `fields` (`full` default, or `compact`) with the output
|
|
36
|
+
fields `fields` and `qos_summary`; compact replaces each `qos` by a one-line summary and
|
|
37
|
+
drops `type_id`, `announced_ns` and `ros_topic_note`.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- `tools/list` is about 37 percent smaller (about 178 KB to 113 KB compact), because some
|
|
42
|
+
clients truncated it: tool and field descriptions are shorter, and the generated schema
|
|
43
|
+
titles and nested-model docstrings are no longer served. Names, types, defaults and
|
|
44
|
+
constraints are unchanged. The long explanations moved to `docs/CONTRACT.md` section 9.
|
|
45
|
+
- `domain_id` on `list_participants`, `participant_events`, `topic_metrics` and
|
|
46
|
+
`list_endpoints`: the description now opens with "Ignored", and a non-zero value that
|
|
47
|
+
differs from the domain joined at startup adds an explicit sentence to the response `note`
|
|
48
|
+
(the response `domain_id` already named the observed domain).
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- `scripts/ground_truth/compare.py` counts only active DDS participants: the `ros2` CLI
|
|
53
|
+
processes that `get_node_info` starts stay listed with status `left` and inflated the count
|
|
54
|
+
(found on the OmniSim kit run of 2026-10-14 on 0.7.0: 76 checks passed, 0 failed).
|
|
55
|
+
|
|
10
56
|
## [0.7.0] - 2026-10-12
|
|
11
57
|
|
|
12
58
|
Breaking release: output contract 2 (see `docs/MIGRATION-0.7.md`), the move to the MCP SDK
|
|
@@ -1615,7 +1661,8 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
|
|
|
1615
1661
|
- The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
|
|
1616
1662
|
- `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
|
|
1617
1663
|
|
|
1618
|
-
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.7.
|
|
1664
|
+
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.7.1...HEAD
|
|
1665
|
+
[0.7.1]: https://github.com/yaniswav/TopicForge/compare/v0.7.0...v0.7.1
|
|
1619
1666
|
[0.7.0]: https://github.com/yaniswav/TopicForge/compare/v0.6.4...v0.7.0
|
|
1620
1667
|
[0.6.4]: https://github.com/yaniswav/TopicForge/compare/v0.6.3...v0.6.4
|
|
1621
1668
|
[0.6.3]: https://github.com/yaniswav/TopicForge/compare/v0.6.2...v0.6.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: topicforge
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: ROS Topic Inspector & Bag Analyzer MCP server for AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/yaniswav/TopicForge
|
|
6
6
|
Project-URL: Repository, https://github.com/yaniswav/TopicForge
|
|
@@ -75,7 +75,7 @@ Every tool also declares itself read-only in the MCP protocol (`readOnlyHint`, `
|
|
|
75
75
|
|
|
76
76
|
It gives the agent fourteen typed tools that return frozen Pydantic schemas (output contract 2: one object per tool, one `note`, units in the field names; see [`docs/CONTRACT.md`](docs/CONTRACT.md)), identical whether the server talks to a real robot or to its built-in mock fixtures. Ask why `nav_planner` gets no scan, and the agent reads the bus, finds the BEST_EFFORT writer facing a RELIABLE reader and names the incompatible policy (see [`examples/02-debug-qos-mismatch.md`](examples/02-debug-qos-mismatch.md)). It is meant for ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
|
|
77
77
|
|
|
78
|
-
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes.
|
|
78
|
+
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. Cyclone DDS, Fast DDS and Dust DDS participants have been observed on live buses: the Fast DDS ones on every CI run of the ROS 2 test bench (Humble and Jazzy with `rmw_fastrtps_cpp`) and in the OmniSim validation. RTI Connext, OpenDDS and CoreDX announce themselves through the same standard discovery, but none of them has been observed yet. This covers discovery only: participants, readers, writers and their QoS. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md).
|
|
79
79
|
|
|
80
80
|
## Quickstart
|
|
81
81
|
|
|
@@ -103,7 +103,7 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
|
|
|
103
103
|
|
|
104
104
|
Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`. If the client cannot launch the server (WSL2 to Windows, a robot over an SSH tunnel), `topicforge --transport streamable-http --port 8765` serves `http://127.0.0.1:8765/mcp` on loopback only; see [`docs/CLIENTS.md`](docs/CLIENTS.md#local-http-transport).
|
|
105
105
|
|
|
106
|
-
**Other MCP clients.** Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in [`docs/CLIENTS.md`](docs/CLIENTS.md). One-click: [Add to Cursor](https://cursor.com/install-mcp?name=topicforge&config=
|
|
106
|
+
**Other MCP clients.** Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in [`docs/CLIENTS.md`](docs/CLIENTS.md). One-click: [Add to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMSIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%3D) | [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%3D%3D0.7.1%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D).
|
|
107
107
|
|
|
108
108
|
Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in [`docs/TESTING.md`](docs/TESTING.md); recurring monitoring prompts and the privacy contract are in [`docs/TUTORIEL.md`](docs/TUTORIEL.md).
|
|
109
109
|
|
|
@@ -174,7 +174,7 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
|
|
|
174
174
|
|
|
175
175
|
External validation against a simulated robot's ground truth (OmniSim): [docs/VALIDATION.md](docs/VALIDATION.md).
|
|
176
176
|
|
|
177
|
-
- DDS validation is partial. The Cyclone adapter has run against
|
|
177
|
+
- DDS validation is partial. The Cyclone adapter has run against real buses with Cyclone, Fast DDS and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`, `.github/workflows/ros2-live.yml`). The Fast DDS adapter (TopicForge joining through the `fastdds` binding) has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed by this project. The multi-vendor claim rests on the RTPS protocol guarantee, not on a recorded cross-vendor run.
|
|
178
178
|
- User-topic payloads are not decoded. `peek_dds_samples` on a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read. `topic_metrics` therefore has data only for the builtin discovery topics and says so in its `status`. It is a discovery-layer probe, not a publish-rate monitor.
|
|
179
179
|
- Liveliness at runtime is not observed. A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned. A crash and a clean leave cannot be told apart, and `lost_ns` is an upper bound of the death.
|
|
180
180
|
- Cyclone vendor ids: participants that do not follow the RTPS vendor-id convention in their GUID prefix (Dust DDS, and RTI by default) are reported with vendor `unknown`.
|
|
@@ -205,7 +205,7 @@ When on, each tool call emits one event with exactly six fields:
|
|
|
205
205
|
| `tool_name` | `"list_topics"` | One of the fourteen tools, never argument values |
|
|
206
206
|
| `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
|
|
207
207
|
| `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
|
|
208
|
-
| `version` | `"0.7.
|
|
208
|
+
| `version` | `"0.7.1"` | TopicForge server version |
|
|
209
209
|
| `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
|
|
210
210
|
| `success` | `true` | Whether the handler returned or raised |
|
|
211
211
|
|
|
@@ -237,7 +237,7 @@ Tests run against the mock adapter, the live adapter's pure parsers and the bind
|
|
|
237
237
|
|
|
238
238
|
## Upgrading
|
|
239
239
|
|
|
240
|
-
TopicForge is pre-1.0; [`CHANGELOG.md`](CHANGELOG.md) lists every change, including the yanked releases and removed extras. 0.7.0
|
|
240
|
+
TopicForge is pre-1.0; [`CHANGELOG.md`](CHANGELOG.md) lists every change, including the yanked releases and removed extras. 0.7.0 changed the shape of tool results (`contract_version` 2): [`docs/MIGRATION-0.7.md`](docs/MIGRATION-0.7.md) maps every old field to its new one.
|
|
241
241
|
|
|
242
242
|
## Layout
|
|
243
243
|
|
|
@@ -14,7 +14,7 @@ Every tool also declares itself read-only in the MCP protocol (`readOnlyHint`, `
|
|
|
14
14
|
|
|
15
15
|
It gives the agent fourteen typed tools that return frozen Pydantic schemas (output contract 2: one object per tool, one `note`, units in the field names; see [`docs/CONTRACT.md`](docs/CONTRACT.md)), identical whether the server talks to a real robot or to its built-in mock fixtures. Ask why `nav_planner` gets no scan, and the agent reads the bus, finds the BEST_EFFORT writer facing a RELIABLE reader and names the incompatible policy (see [`examples/02-debug-qos-mismatch.md`](examples/02-debug-qos-mismatch.md)). It is meant for ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
|
|
16
16
|
|
|
17
|
-
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes.
|
|
17
|
+
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. Cyclone DDS, Fast DDS and Dust DDS participants have been observed on live buses: the Fast DDS ones on every CI run of the ROS 2 test bench (Humble and Jazzy with `rmw_fastrtps_cpp`) and in the OmniSim validation. RTI Connext, OpenDDS and CoreDX announce themselves through the same standard discovery, but none of them has been observed yet. This covers discovery only: participants, readers, writers and their QoS. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md).
|
|
18
18
|
|
|
19
19
|
## Quickstart
|
|
20
20
|
|
|
@@ -42,7 +42,7 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
|
|
|
42
42
|
|
|
43
43
|
Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`. If the client cannot launch the server (WSL2 to Windows, a robot over an SSH tunnel), `topicforge --transport streamable-http --port 8765` serves `http://127.0.0.1:8765/mcp` on loopback only; see [`docs/CLIENTS.md`](docs/CLIENTS.md#local-http-transport).
|
|
44
44
|
|
|
45
|
-
**Other MCP clients.** Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in [`docs/CLIENTS.md`](docs/CLIENTS.md). One-click: [Add to Cursor](https://cursor.com/install-mcp?name=topicforge&config=
|
|
45
|
+
**Other MCP clients.** Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in [`docs/CLIENTS.md`](docs/CLIENTS.md). One-click: [Add to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMSIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%3D) | [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%3D%3D0.7.1%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D).
|
|
46
46
|
|
|
47
47
|
Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in [`docs/TESTING.md`](docs/TESTING.md); recurring monitoring prompts and the privacy contract are in [`docs/TUTORIEL.md`](docs/TUTORIEL.md).
|
|
48
48
|
|
|
@@ -113,7 +113,7 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
|
|
|
113
113
|
|
|
114
114
|
External validation against a simulated robot's ground truth (OmniSim): [docs/VALIDATION.md](docs/VALIDATION.md).
|
|
115
115
|
|
|
116
|
-
- DDS validation is partial. The Cyclone adapter has run against
|
|
116
|
+
- DDS validation is partial. The Cyclone adapter has run against real buses with Cyclone, Fast DDS and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`, `.github/workflows/ros2-live.yml`). The Fast DDS adapter (TopicForge joining through the `fastdds` binding) has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed by this project. The multi-vendor claim rests on the RTPS protocol guarantee, not on a recorded cross-vendor run.
|
|
117
117
|
- User-topic payloads are not decoded. `peek_dds_samples` on a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read. `topic_metrics` therefore has data only for the builtin discovery topics and says so in its `status`. It is a discovery-layer probe, not a publish-rate monitor.
|
|
118
118
|
- Liveliness at runtime is not observed. A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned. A crash and a clean leave cannot be told apart, and `lost_ns` is an upper bound of the death.
|
|
119
119
|
- Cyclone vendor ids: participants that do not follow the RTPS vendor-id convention in their GUID prefix (Dust DDS, and RTI by default) are reported with vendor `unknown`.
|
|
@@ -144,7 +144,7 @@ When on, each tool call emits one event with exactly six fields:
|
|
|
144
144
|
| `tool_name` | `"list_topics"` | One of the fourteen tools, never argument values |
|
|
145
145
|
| `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
|
|
146
146
|
| `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
|
|
147
|
-
| `version` | `"0.7.
|
|
147
|
+
| `version` | `"0.7.1"` | TopicForge server version |
|
|
148
148
|
| `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
|
|
149
149
|
| `success` | `true` | Whether the handler returned or raised |
|
|
150
150
|
|
|
@@ -176,7 +176,7 @@ Tests run against the mock adapter, the live adapter's pure parsers and the bind
|
|
|
176
176
|
|
|
177
177
|
## Upgrading
|
|
178
178
|
|
|
179
|
-
TopicForge is pre-1.0; [`CHANGELOG.md`](CHANGELOG.md) lists every change, including the yanked releases and removed extras. 0.7.0
|
|
179
|
+
TopicForge is pre-1.0; [`CHANGELOG.md`](CHANGELOG.md) lists every change, including the yanked releases and removed extras. 0.7.0 changed the shape of tool results (`contract_version` 2): [`docs/MIGRATION-0.7.md`](docs/MIGRATION-0.7.md) maps every old field to its new one.
|
|
180
180
|
|
|
181
181
|
## Layout
|
|
182
182
|
|
|
@@ -6,7 +6,7 @@ TopicForge (ROS 2 / DDS) is a local, read-only MCP server that speaks stdio.
|
|
|
6
6
|
Every client below launches it as a child process with the same command:
|
|
7
7
|
|
|
8
8
|
```
|
|
9
|
-
uvx --from "topicforge==0.7.
|
|
9
|
+
uvx --from "topicforge==0.7.1" topicforge
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
and the same three environment variables:
|
|
@@ -21,7 +21,7 @@ The registry name is `io.github.yaniswav/topicforge`. Where a client asks for a
|
|
|
21
21
|
display name, use **TopicForge (ROS 2 / DDS)**: an unrelated SEO product is also
|
|
22
22
|
called TopicForge in the MCP registries.
|
|
23
23
|
|
|
24
|
-
The version pin (`==0.7.
|
|
24
|
+
The version pin (`==0.7.1`) is moved on every release together with the other
|
|
25
25
|
version strings; a test keeps them in sync.
|
|
26
26
|
|
|
27
27
|
## Before you start
|
|
@@ -44,7 +44,7 @@ version strings; a test keeps them in sync.
|
|
|
44
44
|
`"--from"` in the args. To build the binding yourself, install the Cyclone C library and
|
|
45
45
|
run `pip install "topicforge[dds]"`. The `[dds]` and `[dds-cyclone]` extras still work
|
|
46
46
|
as aliases, so older commands keep installing.
|
|
47
|
-
- **pip alternative.** `pip install "topicforge==0.7.
|
|
47
|
+
- **pip alternative.** `pip install "topicforge==0.7.1"`, then use
|
|
48
48
|
`"command": "topicforge"` (or `"command": "python", "args": ["-m", "topicforge"]`)
|
|
49
49
|
with no `args` for uvx. Use the absolute path of the binary if the client does
|
|
50
50
|
not see your venv.
|
|
@@ -72,7 +72,7 @@ say where the file lives and what differs.
|
|
|
72
72
|
"mcpServers": {
|
|
73
73
|
"topicforge": {
|
|
74
74
|
"command": "uvx",
|
|
75
|
-
"args": ["--from", "topicforge==0.7.
|
|
75
|
+
"args": ["--from", "topicforge==0.7.1", "topicforge"],
|
|
76
76
|
"env": {
|
|
77
77
|
"TOPICFORGE_MODE": "auto",
|
|
78
78
|
"TOPICFORGE_DDS_BACKEND": "cyclone",
|
|
@@ -95,7 +95,7 @@ claude plugin install topicforge@topicforge
|
|
|
95
95
|
Or just the server:
|
|
96
96
|
|
|
97
97
|
```
|
|
98
|
-
claude mcp add topicforge --env TOPICFORGE_MODE=auto --env TOPICFORGE_DDS_BACKEND=cyclone --env TOPICFORGE_DDS_DOMAIN_ID=0 -- uvx --from "topicforge==0.7.
|
|
98
|
+
claude mcp add topicforge --env TOPICFORGE_MODE=auto --env TOPICFORGE_DDS_BACKEND=cyclone --env TOPICFORGE_DDS_DOMAIN_ID=0 -- uvx --from "topicforge==0.7.1" topicforge
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
## Claude Desktop
|
|
@@ -112,9 +112,9 @@ the common JSON shape above and restart the app completely. On Windows, if
|
|
|
112
112
|
|
|
113
113
|
The one-click links in this page and in the README are https redirects, because GitHub and PyPI strip `cursor://` and `vscode:` links. Only the raw schemes are documented by Cursor and VS Code; the https forms (`cursor.com/install-mcp`, `vscode.dev/redirect/mcp/install`) were observed to redirect correctly on 2026-10-06 but are not in the vendors' docs (unverified, last checked 2026-10-06). The raw links are given as a fallback.
|
|
114
114
|
|
|
115
|
-
One click: [Add TopicForge to Cursor](https://cursor.com/install-mcp?name=topicforge&config=
|
|
115
|
+
One click: [Add TopicForge to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMSIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%3D)
|
|
116
116
|
|
|
117
|
-
If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=
|
|
117
|
+
If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMSIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0=`
|
|
118
118
|
|
|
119
119
|
Or paste the common JSON shape into `~/.cursor/mcp.json` (all projects) or
|
|
120
120
|
`.cursor/mcp.json` (one project).
|
|
@@ -125,9 +125,9 @@ The `plugin/` folder of this repository is also an [Agent Plugins](https://agent
|
|
|
125
125
|
|
|
126
126
|
## VS Code / GitHub Copilot
|
|
127
127
|
|
|
128
|
-
One click: [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%3D%3D0.7.
|
|
128
|
+
One click: [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%3D%3D0.7.1%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D)
|
|
129
129
|
|
|
130
|
-
Fallback, the raw URL handler: `vscode:mcp/install?%7B%22name%22%3A%22topicforge%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%3D%3D0.7.
|
|
130
|
+
Fallback, the raw URL handler: `vscode:mcp/install?%7B%22name%22%3A%22topicforge%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%3D%3D0.7.1%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D`
|
|
131
131
|
|
|
132
132
|
Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or run
|
|
133
133
|
"MCP: Add Server" from the Command Palette:
|
|
@@ -138,7 +138,7 @@ Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or r
|
|
|
138
138
|
"topicforge": {
|
|
139
139
|
"type": "stdio",
|
|
140
140
|
"command": "uvx",
|
|
141
|
-
"args": ["--from", "topicforge==0.7.
|
|
141
|
+
"args": ["--from", "topicforge==0.7.1", "topicforge"],
|
|
142
142
|
"env": {
|
|
143
143
|
"TOPICFORGE_MODE": "auto",
|
|
144
144
|
"TOPICFORGE_DDS_BACKEND": "cyclone",
|
|
@@ -167,7 +167,7 @@ Servers", and paste the common JSON shape (the file is
|
|
|
167
167
|
Project file `.roo/mcp.json` (takes precedence) or the global `mcp_settings.json`
|
|
168
168
|
opened from the MCP view. Paste the common JSON shape. Roo's docs say Windows
|
|
169
169
|
needs a `cmd /c` wrapper for `npx`; for `uvx`, try it directly first and fall back
|
|
170
|
-
to `"command": "cmd", "args": ["/c", "uvx", "--from", "topicforge==0.7.
|
|
170
|
+
to `"command": "cmd", "args": ["/c", "uvx", "--from", "topicforge==0.7.1",
|
|
171
171
|
"topicforge"]` if it fails to start.
|
|
172
172
|
|
|
173
173
|
## Continue
|
|
@@ -184,7 +184,7 @@ mcpServers:
|
|
|
184
184
|
command: uvx
|
|
185
185
|
args:
|
|
186
186
|
- "--from"
|
|
187
|
-
- "topicforge==0.7.
|
|
187
|
+
- "topicforge==0.7.1"
|
|
188
188
|
- "topicforge"
|
|
189
189
|
env:
|
|
190
190
|
TOPICFORGE_MODE: auto
|
|
@@ -201,7 +201,7 @@ In `settings.json`, under `context_servers`:
|
|
|
201
201
|
"context_servers": {
|
|
202
202
|
"topicforge": {
|
|
203
203
|
"command": "uvx",
|
|
204
|
-
"args": ["--from", "topicforge==0.7.
|
|
204
|
+
"args": ["--from", "topicforge==0.7.1", "topicforge"],
|
|
205
205
|
"env": {
|
|
206
206
|
"TOPICFORGE_MODE": "auto",
|
|
207
207
|
"TOPICFORGE_DDS_BACKEND": "cyclone",
|
|
@@ -227,7 +227,7 @@ run with the dds extra can exceed, so set it to 60 s:
|
|
|
227
227
|
```toml
|
|
228
228
|
[mcp_servers.topicforge]
|
|
229
229
|
command = "uvx"
|
|
230
|
-
args = ["--from", "topicforge==0.7.
|
|
230
|
+
args = ["--from", "topicforge==0.7.1", "topicforge"]
|
|
231
231
|
startup_timeout_sec = 60
|
|
232
232
|
|
|
233
233
|
[mcp_servers.topicforge.env]
|
|
@@ -258,7 +258,7 @@ extensions:
|
|
|
258
258
|
name: TopicForge (ROS 2 / DDS)
|
|
259
259
|
type: stdio
|
|
260
260
|
cmd: uvx
|
|
261
|
-
args: [--from, "topicforge==0.7.
|
|
261
|
+
args: [--from, "topicforge==0.7.1", topicforge]
|
|
262
262
|
enabled: true
|
|
263
263
|
envs: { "TOPICFORGE_MODE": "auto", "TOPICFORGE_DDS_BACKEND": "cyclone", "TOPICFORGE_DDS_DOMAIN_ID": "0" }
|
|
264
264
|
timeout: 300
|
|
@@ -325,7 +325,7 @@ when the question matches.
|
|
|
325
325
|
|
|
326
326
|
## Troubleshooting
|
|
327
327
|
|
|
328
|
-
- Run the command by hand first: `uvx --from "topicforge==0.7.
|
|
328
|
+
- Run the command by hand first: `uvx --from "topicforge==0.7.1" topicforge --version`.
|
|
329
329
|
- Server starts but sees no DDS participants: check the domain id, and that the
|
|
330
330
|
client machine is on the same network as the robot (DDS discovery uses
|
|
331
331
|
multicast UDP). Ask the assistant to call `health_check`.
|
|
@@ -14,12 +14,13 @@ Sections:
|
|
|
14
14
|
|
|
15
15
|
1. Principles
|
|
16
16
|
2. Decisions
|
|
17
|
-
3.
|
|
17
|
+
3. Optional fields
|
|
18
18
|
4. Rate verdicts
|
|
19
19
|
5. Reserved multimodal exception
|
|
20
20
|
6. Change policy and tests
|
|
21
21
|
7. 0.7.0 migration list
|
|
22
22
|
8. Node tools
|
|
23
|
+
9. 0.7.x additions
|
|
23
24
|
|
|
24
25
|
## 1. Principles
|
|
25
26
|
|
|
@@ -249,34 +250,50 @@ values a tool can return:
|
|
|
249
250
|
{"timestamp_ns": 1760000000123456789, "stamp_source": "dds_source"}
|
|
250
251
|
```
|
|
251
252
|
|
|
252
|
-
## 3.
|
|
253
|
+
## 3. Optional fields
|
|
253
254
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
Some shapes were fixed before any tool filled them, so that they could be added without a
|
|
256
|
+
break. Each is optional and additive; a tool that does not fill one omits its content (1.3,
|
|
257
|
+
mechanism 1).
|
|
257
258
|
|
|
258
259
|
### 3.1 `suggested_fixes`
|
|
259
260
|
|
|
260
|
-
|
|
261
|
-
|
|
261
|
+
Populated by `detect_qos_mismatches` since 0.7.1, on `reports[]` (RxO mismatches) and on
|
|
262
|
+
`not_matched[]` (`partition` splits only). Not a tool: an optional list on a diagnostic
|
|
263
|
+
finding. TopicForge diagnoses, proposes a fix as text, a human applies it, TopicForge
|
|
264
|
+
verifies. Other tools may fill it later with the same shape.
|
|
262
265
|
|
|
263
266
|
```json
|
|
264
267
|
{"suggested_fixes": [
|
|
265
268
|
{"kind": "qos_override",
|
|
266
269
|
"target": "reader /nav2/controller on /scan",
|
|
267
|
-
"text": "
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
"text": "/nav2/controller:
|
|
271
|
+
ros__parameters:
|
|
272
|
+
qos_overrides./scan.subscription.reliability: best_effort",
|
|
273
|
+
"tradeoff": "The reader stops requesting retransmission, so it can miss samples. Only works if the node enabled QoS overrides (rclcpp/rclpy QosOverridingOptions); pass the file when the node starts: --ros-args --params-file <file>.",
|
|
274
|
+
"verify_with": "detect_qos_mismatches(topic='rt/scan')"}
|
|
270
275
|
]}
|
|
271
276
|
```
|
|
272
277
|
|
|
273
278
|
| Field | Content |
|
|
274
279
|
| --- | --- |
|
|
275
|
-
| `kind` | one of `cli`, `qos_override`, `dds_profile`, `launch_param` (closed set) |
|
|
280
|
+
| `kind` | one of `cli`, `qos_override`, `dds_profile`, `launch_param` (closed set). 0.7.1 emits `qos_override` and `dds_profile` |
|
|
276
281
|
| `target` | which node or which side to change, writer or reader |
|
|
277
282
|
| `text` | the exact command, YAML or XML to apply |
|
|
278
283
|
| `tradeoff` | what is lost by applying it |
|
|
279
|
-
| `verify_with` | the TopicForge tool to run again to confirm the repair |
|
|
284
|
+
| `verify_with` | the TopicForge tool and arguments to run again to confirm the repair |
|
|
285
|
+
|
|
286
|
+
Behaviour:
|
|
287
|
+
|
|
288
|
+
- A finding gets one fix per side that can change, most often two (the safer side first,
|
|
289
|
+
explained in `tradeoff`). A finding with no supported fix (DestinationOrder,
|
|
290
|
+
DataRepresentation, History) has an empty list and a `hints` entry.
|
|
291
|
+
- `qos_override` is used for a ROS 2 topic (`rt/` prefix) when only Reliability and
|
|
292
|
+
Durability differ; everything else is a `dds_profile`: a Fast DDS XML profile followed by
|
|
293
|
+
the cyclonedds-python equivalent.
|
|
294
|
+
- Findings with the same topic and compared values share one set of fixes, carried by the
|
|
295
|
+
first report; the others have an empty list and a `hints` entry says so. A fix lists at most
|
|
296
|
+
three nodes.
|
|
280
297
|
|
|
281
298
|
Rules:
|
|
282
299
|
|
|
@@ -284,7 +301,9 @@ Rules:
|
|
|
284
301
|
text for a human; TopicForge never applies them". `readOnlyHint` stays true.
|
|
285
302
|
- No destructive verb: nothing that kills a process, shuts down a lifecycle node,
|
|
286
303
|
deletes or unsets a parameter, removes a file. A test fails any suggestion whose
|
|
287
|
-
`text` contains a forbidden
|
|
304
|
+
`text`, `target` or `tradeoff` contains a forbidden word (kill, pkill, delete, rm,
|
|
305
|
+
shutdown, deactivate, cleanup, set, publish, call, launch, restart, reset); the list
|
|
306
|
+
lives in that test.
|
|
288
307
|
- When two sides can fix the problem (the writer or the reader), both are offered as
|
|
289
308
|
separate entries, each with its own `tradeoff`.
|
|
290
309
|
|
|
@@ -628,3 +647,130 @@ Rules:
|
|
|
628
647
|
- A name used by several nodes is flagged: `duplicate_count` above 1 and a `note`, because the
|
|
629
648
|
CLI answers for one of them without saying which.
|
|
630
649
|
- An unknown node is an `isError` result naming close matches (1.6).
|
|
650
|
+
|
|
651
|
+
## 9. 0.7.x additions
|
|
652
|
+
|
|
653
|
+
Everything here is additive (1.9): a new optional field or input with a backward-compatible
|
|
654
|
+
default, or a description that says the same thing in fewer words. No property was renamed,
|
|
655
|
+
removed or retyped; `scripts/contract/snapshot_tools.py` regenerates the snapshots and the diff
|
|
656
|
+
shows only added properties.
|
|
657
|
+
|
|
658
|
+
### 9.1 Listing size
|
|
659
|
+
|
|
660
|
+
`tools/list` carries the description, input schema and output schema of every tool, and some
|
|
661
|
+
clients truncate it. In 0.7.0 it was about 178 KB compact (14 tools); 0.7.1 serves about 113 KB.
|
|
662
|
+
How:
|
|
663
|
+
|
|
664
|
+
- Pydantic's generated `title` is dropped from every property and nested model, and the
|
|
665
|
+
docstring of a nested model is dropped (the model name stays). The output type name stays as
|
|
666
|
+
the `title` of each `outputSchema`. Done by `tools/schema_compact.py`.
|
|
667
|
+
- Field descriptions are one sentence, and self-explanatory ones (`reader_guid`, `returned`)
|
|
668
|
+
have none. The longer explanations are in this section and in section 4; the rules a client
|
|
669
|
+
needs stay in the tool description.
|
|
670
|
+
- `tests/test_friction_fixes.py` fails if the listing grows back over 120 KB.
|
|
671
|
+
|
|
672
|
+
Rule for new fields: one sentence, no history, no restating the name.
|
|
673
|
+
|
|
674
|
+
### 9.2 `restarts_by_name`
|
|
675
|
+
|
|
676
|
+
A restarted ROS 2 node is a new DDS participant with a new GUID and the same name, so
|
|
677
|
+
`seen_count` (observations of one GUID) stays at 1 and an agent had to count GUIDs by hand.
|
|
678
|
+
`list_participants` and `participant_events` now carry:
|
|
679
|
+
|
|
680
|
+
- `restarts_by_name: {name: {lives, departures}}`. `lives` is the number of distinct GUIDs
|
|
681
|
+
that carried the name (restarts = `lives` - 1); `departures` is the number of times a
|
|
682
|
+
participant with that name was lost.
|
|
683
|
+
- Only names with `lives` > 1 or `departures` > 0 are listed, so a healthy bus adds nothing. A
|
|
684
|
+
name that is absent ran once and stayed.
|
|
685
|
+
- `list_participants` counts the participants it lists (`status` `left` is a departure), bounded
|
|
686
|
+
by the lifecycle buffer. `participant_events` counts the events in the `lookback_s` window
|
|
687
|
+
only: a participant discovered before the window and still active does not appear, and one
|
|
688
|
+
that left inside it counts one life and one departure.
|
|
689
|
+
- Participants and events without a `name` are excluded and `restarts_note` says how many.
|
|
690
|
+
Dust DDS announces no name, and every Fast DDS participant of a ROS 2 setup is named `/`, so
|
|
691
|
+
those cannot be separated by name.
|
|
692
|
+
|
|
693
|
+
### 9.3 `domain_id` is ignored, and says so
|
|
694
|
+
|
|
695
|
+
`domain_id` on `list_participants`, `participant_events`, `topic_metrics` and `list_endpoints`
|
|
696
|
+
never selected a domain: the adapter reports the one it joined at startup
|
|
697
|
+
(`TOPICFORGE_DDS_DOMAIN_ID`, `health_check.dds_domain_id`). The parameter stays (removing it
|
|
698
|
+
is a break). Its description now starts with "Ignored" and the response `domain_id` is the
|
|
699
|
+
domain actually observed.
|
|
700
|
+
|
|
701
|
+
When a non-zero `domain_id` differs from the joined domain, the response `note` says
|
|
702
|
+
`domain_id N was ignored: this server observes domain M ...`. Chosen over raising an
|
|
703
|
+
`AdapterError` because a note is additive and cannot break a client that always passed the
|
|
704
|
+
wrong value, while an error would; the envelope already carries the real domain. The default
|
|
705
|
+
`0` cannot be told from "not given", so an explicit `0` is never flagged, and an omitted
|
|
706
|
+
argument never produces a note when the joined domain is not 0. The mock adapter does not
|
|
707
|
+
report a joined domain and gives no note.
|
|
708
|
+
|
|
709
|
+
### 9.4 `list_endpoints` `fields`
|
|
710
|
+
|
|
711
|
+
New optional input `fields`: `"full"` (default, unchanged output) or `"compact"`. Compact
|
|
712
|
+
keeps every endpoint and the `by_topic` roll-up, sets `qos` to `null` and fills the new
|
|
713
|
+
`qos_summary` with a one-line reading (`RELIABLE VOLATILE KEEP_LAST(10) deadline=100ms
|
|
714
|
+
partitions=a`, defaults left out), and drops `type_id`, `announced_ns` and `ros_topic_note`.
|
|
715
|
+
The response echoes the level in `fields`. Use `full` on one topic or participant to read a
|
|
716
|
+
complete QoS. In compact mode `qos_summary` is `qos unresolved` when the QoS could not be
|
|
717
|
+
resolved.
|
|
718
|
+
|
|
719
|
+
### 9.5 `MismatchScan.summary`
|
|
720
|
+
|
|
721
|
+
One line stating the counts: `0 incompatible, 0 risky QoS pair(s); 3 pairs will never match
|
|
722
|
+
(3 by partition): no data flows between them, see `not_matched`; 5 pairs will match given the
|
|
723
|
+
announced QoS.` An empty `reports` next to a non-empty `not_matched` can no longer be read as
|
|
724
|
+
an all-clear from the summary alone. The counts are over the whole scan, before the 200-entry
|
|
725
|
+
cap. Built in `adapters/common/qos_scan.py`, so every backend and the mock carry it.
|
|
726
|
+
|
|
727
|
+
### 9.6 `ParticipantEvent.age_s`
|
|
728
|
+
|
|
729
|
+
Seconds between `timestamp_ns` and the moment the response was built (never negative, three
|
|
730
|
+
decimals), next to the `*_ns` fields that are hard to read. `null` in mock mode, whose timeline
|
|
731
|
+
is a fixed 2024 anchor. Only the events got it: the other `*_ns` stamps of the listing
|
|
732
|
+
(`first_seen_ns`, `last_seen_ns`) would add a field to every participant for little reading
|
|
733
|
+
value; compare against `health_check.now_ns` when needed.
|
|
734
|
+
|
|
735
|
+
### 9.7 Explanations moved out of the schema
|
|
736
|
+
|
|
737
|
+
- `TopicRate.startup_burst_count`: on a live call the fresh `ros2 topic echo` prints the
|
|
738
|
+
messages its reader queued before the first callback (up to 5) in one go. A leading run of
|
|
739
|
+
intervals under a quarter of the median interval is that drain; those messages are set aside
|
|
740
|
+
before the intervals, `observed_frequency_hz` and the verdict, are still counted in
|
|
741
|
+
`message_count`, and still feed `sim_frequency_hz`. Always 0 on `recorded_ns`.
|
|
742
|
+
- `TopicRate.observed_frequency_hz` is coarse when the messages span under about a second (one
|
|
743
|
+
late message moves it by several percent): compare `sim_frequency_hz` when present and ask
|
|
744
|
+
for `count` 50 on a topic above 20 Hz. `sim_frequency_hz` is `null` when the messages carry no
|
|
745
|
+
stamp (`stamp_source` `none` or `recorded`) or the stamps do not advance.
|
|
746
|
+
- `TopicRate.verdict`: a `silent` topic may be latched; a topic with several publishers such as
|
|
747
|
+
`/tf` reads `erratic` without being broken; a simulated or bridged sensor whose intervals
|
|
748
|
+
alternate reads `jittery` while healthy: compare `max_gap_s` with `interval_median_s` before
|
|
749
|
+
suspecting a lost message.
|
|
750
|
+
- `MessageSample.timestamp_ns` on simulated time is sim time since the simulation started (a
|
|
751
|
+
few seconds, not a date) and 0 is a valid value. `peek_bag_samples` gives a message without a
|
|
752
|
+
header the bag record time (`stamp_source` `recorded`), also in `recorded_ns`. Mock samples
|
|
753
|
+
with a header carry synthetic increasing values. `stamp_source`: `sample_messages` gives
|
|
754
|
+
`header`, `payload` or `none`; `peek_bag_samples` `header`, `payload` or `recorded`;
|
|
755
|
+
`peek_dds_samples` `dds_source` or `none`.
|
|
756
|
+
- `MessageSample.payload`: `_truncated_fields` lists the dotted paths of arrays, strings or bytes
|
|
757
|
+
cut at `max_array_length` (a string of exactly that length plus `...` that was not cut is
|
|
758
|
+
listed too); `_raw_text` holds the unparsed output only when a message could not be parsed;
|
|
759
|
+
with `arrays_summary_only` an array is a string such as `<sequence type: float, length: 541>`.
|
|
760
|
+
A message over the size cap may be dropped with a `note`.
|
|
761
|
+
- `MessageSample.summary` is computed on the whole message even when `payload` is cut, and is
|
|
762
|
+
`null` for a scan whose arrays were replaced by `arrays_summary_only`.
|
|
763
|
+
- `ParticipantInfo.lost_ns` and `ParticipantEvent.time_source` for `lost`: an upper bound of when
|
|
764
|
+
the participant died. After a clean shutdown it is the exact leave time; after a crash it is
|
|
765
|
+
when the lease expired, and the two cannot be told apart. The process died at or before it,
|
|
766
|
+
and at most one lease earlier (10 s Cyclone default, 20 s Fast DDS, 100 s RTI).
|
|
767
|
+
- `ParticipantInfo.vendor` is read from the vendor prefix of the participant GUID. Dust DDS and
|
|
768
|
+
RTI Connext do not put their vendor id there, and the Cyclone Python binding does not expose
|
|
769
|
+
the RTPS header vendor id, so those are `unknown` (the participant is still reported). See
|
|
770
|
+
`docs/dds-interop-matrix.md`.
|
|
771
|
+
- `QosProfile.partitions`: no Partition policy is the default partition, reported as `[""]`,
|
|
772
|
+
never `[]`; `null` only when the backend cannot read partitions at all. `history` is reported
|
|
773
|
+
only for TopicForge's own endpoints and for Cyclone peers that set it (see `history_note`);
|
|
774
|
+
a reader `deadline_ns` tighter than the writer's is incompatible.
|
|
775
|
+
- `HealthReport.ros2_distro` discloses `ROS_DISTRO` by design under the local-trust threat model
|
|
776
|
+
(README "Security model"); a hosted multi-tenant endpoint would scrub it.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A short tour of TopicForge's DDS observability tools. The server joins the bus as a read-only DDS-RTPS participant and reads the builtin discovery topics, which the OMG protocol standardizes for every conformant vendor (observed so far: Cyclone DDS and Dust DDS). The `MiddlewareAdapter` protocol has no write method, so the MCP client cannot publish back on any backend. This guide does not assume ROS2.
|
|
4
4
|
|
|
5
|
-
Validation status. The Cyclone adapter has run against a live bus, with a Python / Cyclone participant and Dust DDS participants in Rust, Python and C, on Windows and in CI (Ubuntu and Windows, `.github/workflows/demo.yml`); see [`scripts/integration/README.md`](../scripts/integration/README.md).
|
|
5
|
+
Validation status. The Cyclone adapter has run against a live bus, with a Python / Cyclone participant and Dust DDS participants in Rust, Python and C, on Windows and in CI (Ubuntu and Windows, `.github/workflows/demo.yml`); see [`scripts/integration/README.md`](../scripts/integration/README.md). Fast DDS participants (ROS 2 nodes on `rmw_fastrtps_cpp`) are observed by the Cyclone adapter on every run of the ROS 2 test bench in CI; the Fast DDS adapter itself (joining through the `fastdds` binding) has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed yet. For runnable live scenarios (who is on the bus, why two nodes cannot talk, a node that crashed, a late joiner that misses data), see [`examples/dds/README.md`](../examples/dds/README.md). Multi-vendor positioning: [`dds-interop-matrix.md`](dds-interop-matrix.md).
|
|
6
6
|
|
|
7
7
|
## 1. Mock mode
|
|
8
8
|
|
|
@@ -112,6 +112,20 @@ Claude run reported none.
|
|
|
112
112
|
- **Unnamed or unknown-vendor participant** read as a possible problem in 2 of
|
|
113
113
|
3 healthy-bus runs.
|
|
114
114
|
|
|
115
|
+
Addressed in 0.7.1 (changes made after the runs above, not re-measured by a new
|
|
116
|
+
evaluation round):
|
|
117
|
+
|
|
118
|
+
- Listing size: `tools/list` went from about 178 KB to about 113 KB compact
|
|
119
|
+
(`docs/CONTRACT.md` section 9.1).
|
|
120
|
+
- Empty `reports` next to `not_matched`: `MismatchScan.summary` states the counts
|
|
121
|
+
(section 9.5).
|
|
122
|
+
- `domain_id`: the description opens with "Ignored" and the response `note` flags a
|
|
123
|
+
differing non-zero value (section 9.3).
|
|
124
|
+
- `*_ns` fields: `age_s` on participant events (section 9.6).
|
|
125
|
+
- `seen_count` and restarts: `restarts_by_name` on `list_participants` and
|
|
126
|
+
`participant_events` (section 9.2).
|
|
127
|
+
- Verbose `list_endpoints`: `fields` `compact` (section 9.4).
|
|
128
|
+
|
|
115
129
|
## Description edit made
|
|
116
130
|
|
|
117
131
|
One, description only, no contract change: `list_participants` now states that
|
|
@@ -15,7 +15,7 @@ How to get a working ROS2 environment to point TopicForge at, and how to wire it
|
|
|
15
15
|
```bash
|
|
16
16
|
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
|
|
17
17
|
pip install topicforge
|
|
18
|
-
python -m topicforge --version # -> topicforge 0.7.
|
|
18
|
+
python -m topicforge --version # -> topicforge 0.7.1
|
|
19
19
|
TOPICFORGE_MODE=mock python -m topicforge # blocks on stdio; MCP clients spawn it
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -41,7 +41,7 @@ python3 -m venv ~/topicforge-venv && source ~/topicforge-venv/bin/activate
|
|
|
41
41
|
pip install topicforge # Ubuntu 22.04 ships Python 3.10, which is enough
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Then run live mode with three terminals. In the first, `ros2 run demo_nodes_cpp talker` publishes `/chatter` at about 1 Hz. In the second, `ros2 topic list` should show `/chatter`. In the third, source ROS2, activate the venv and run `TOPICFORGE_MODE=live python -m topicforge`. The startup line reads `topicforge 0.7.
|
|
44
|
+
Then run live mode with three terminals. In the first, `ros2 run demo_nodes_cpp talker` publishes `/chatter` at about 1 Hz. In the second, `ros2 topic list` should show `/chatter`. In the third, source ROS2, activate the venv and run `TOPICFORGE_MODE=live python -m topicforge`. The startup line reads `topicforge 0.7.1 ready (mode=live, requested_mode=live, adapter=ros2_cli, telemetry=off)`; `mode=mock` means `ros2` was not found and the server fell back to fixtures.
|
|
45
45
|
|
|
46
46
|
## Linux native
|
|
47
47
|
|