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.
Files changed (252) hide show
  1. {topicforge-0.7.0 → topicforge-0.7.1}/CHANGELOG.md +48 -1
  2. {topicforge-0.7.0 → topicforge-0.7.1}/PKG-INFO +6 -6
  3. {topicforge-0.7.0 → topicforge-0.7.1}/README.md +5 -5
  4. {topicforge-0.7.0 → topicforge-0.7.1}/docs/CLIENTS.md +16 -16
  5. {topicforge-0.7.0 → topicforge-0.7.1}/docs/CONTRACT.md +159 -13
  6. {topicforge-0.7.0 → topicforge-0.7.1}/docs/DDS_QUICKSTART.md +1 -1
  7. {topicforge-0.7.0 → topicforge-0.7.1}/docs/EVAL.md +14 -0
  8. {topicforge-0.7.0 → topicforge-0.7.1}/docs/TESTING.md +2 -2
  9. topicforge-0.7.1/docs/TOOLS.md +459 -0
  10. topicforge-0.7.1/docs/VALIDATION.md +44 -0
  11. {topicforge-0.7.0 → topicforge-0.7.1}/docs/dds-interop-matrix.md +2 -2
  12. {topicforge-0.7.0 → topicforge-0.7.1}/plugin/README.md +1 -1
  13. {topicforge-0.7.0 → topicforge-0.7.1}/plugin/pyproject.toml +2 -2
  14. {topicforge-0.7.0 → topicforge-0.7.1}/pyproject.toml +1 -1
  15. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/__init__.py +1 -1
  16. topicforge-0.7.1/src/topicforge/adapters/common/qos_fixes.py +518 -0
  17. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_scan.py +50 -6
  18. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/models/__init__.py +2 -0
  19. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/models/nodes.py +11 -21
  20. topicforge-0.7.1/src/topicforge/models/schemas.py +1269 -0
  21. topicforge-0.7.1/src/topicforge/models/summaries.py +242 -0
  22. topicforge-0.7.1/src/topicforge/services/dds_views.py +154 -0
  23. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/inspector.py +50 -9
  24. topicforge-0.7.1/src/topicforge/tools/handlers.py +699 -0
  25. topicforge-0.7.1/src/topicforge/tools/schema_compact.py +60 -0
  26. topicforge-0.7.1/tests/contract/analyze_bag.json +219 -0
  27. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/detect_qos_mismatches.json +54 -129
  28. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/get_node_info.json +13 -40
  29. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/get_topic_info.json +9 -30
  30. topicforge-0.7.1/tests/contract/health_check.json +303 -0
  31. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/list_endpoints.json +73 -128
  32. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/list_nodes.json +4 -18
  33. topicforge-0.7.1/tests/contract/list_participants.json +298 -0
  34. topicforge-0.7.1/tests/contract/list_topics.json +96 -0
  35. topicforge-0.7.1/tests/contract/participant_events.json +239 -0
  36. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/peek_bag_samples.json +92 -209
  37. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/peek_dds_samples.json +91 -207
  38. topicforge-0.7.1/tests/contract/sample_messages.json +1104 -0
  39. topicforge-0.7.1/tests/contract/topic_metrics.json +181 -0
  40. topicforge-0.7.1/tests/test_friction_fixes.py +315 -0
  41. topicforge-0.7.1/tests/test_qos_fixes.py +362 -0
  42. topicforge-0.7.0/docs/TOOLS.md +0 -452
  43. topicforge-0.7.0/docs/VALIDATION.md +0 -19
  44. topicforge-0.7.0/src/topicforge/models/schemas.py +0 -1555
  45. topicforge-0.7.0/src/topicforge/models/summaries.py +0 -328
  46. topicforge-0.7.0/src/topicforge/tools/handlers.py +0 -870
  47. topicforge-0.7.0/tests/contract/analyze_bag.json +0 -243
  48. topicforge-0.7.0/tests/contract/health_check.json +0 -332
  49. topicforge-0.7.0/tests/contract/list_participants.json +0 -293
  50. topicforge-0.7.0/tests/contract/list_topics.json +0 -117
  51. topicforge-0.7.0/tests/contract/participant_events.json +0 -216
  52. topicforge-0.7.0/tests/contract/sample_messages.json +0 -1223
  53. topicforge-0.7.0/tests/contract/topic_metrics.json +0 -201
  54. {topicforge-0.7.0 → topicforge-0.7.1}/.gitignore +0 -0
  55. {topicforge-0.7.0 → topicforge-0.7.1}/LICENSE +0 -0
  56. {topicforge-0.7.0 → topicforge-0.7.1}/docs/MIGRATION-0.7.md +0 -0
  57. {topicforge-0.7.0 → topicforge-0.7.1}/docs/TROUBLESHOOTING.md +0 -0
  58. {topicforge-0.7.0 → topicforge-0.7.1}/docs/TUTORIEL.md +0 -0
  59. {topicforge-0.7.0 → topicforge-0.7.1}/examples/README.md +0 -0
  60. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/00_hello_pub_sub/README.md +0 -0
  61. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/01_who_is_on_the_bus/README.md +0 -0
  62. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/02_why_cant_they_talk/README.md +0 -0
  63. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/03_a_node_crashed/README.md +0 -0
  64. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/04_late_joiner_misses_data/README.md +0 -0
  65. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/05_reliability_in_code/README.md +0 -0
  66. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
  67. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/07_deadline_in_code/README.md +0 -0
  68. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/08_crash_seen_from_inside/README.md +0 -0
  69. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/10_lidar_silent_after_driver_swap/README.md +0 -0
  70. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/11_who_talks_to_whom/README.md +0 -0
  71. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/12_safety_monitor_dropout/README.md +0 -0
  72. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/13_deadline_not_offered/README.md +0 -0
  73. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/14_restart_loop/README.md +0 -0
  74. {topicforge-0.7.0 → topicforge-0.7.1}/examples/dds/README.md +0 -0
  75. {topicforge-0.7.0 → topicforge-0.7.1}/plugin/LICENSE +0 -0
  76. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/agent_eval/README.md +0 -0
  77. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/ground_truth/README.md +0 -0
  78. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/README.md +0 -0
  79. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/cyclone_c/README.md +0 -0
  80. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
  81. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
  82. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/dust_py/README.md +0 -0
  83. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
  84. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/fast_py/README.md +0 -0
  85. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
  86. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/rti_c/README.md +0 -0
  87. {topicforge-0.7.0 → topicforge-0.7.1}/scripts/integration/publishers/rti_cpp/README.md +0 -0
  88. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/__main__.py +0 -0
  89. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/__init__.py +0 -0
  90. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/base.py +0 -0
  91. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/__init__.py +0 -0
  92. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/bag_kind.py +0 -0
  93. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/cdr_decoder.py +0 -0
  94. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/dds_helpers.py +0 -0
  95. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/dds_introspection.py +0 -0
  96. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/discovery_tracker.py +0 -0
  97. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/endpoints.py +0 -0
  98. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/lifecycle.py +0 -0
  99. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
  100. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/nodes.py +0 -0
  101. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_analyzer.py +0 -0
  102. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_endpoints.py +0 -0
  103. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/qos_normalize.py +0 -0
  104. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/ros_names.py +0 -0
  105. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/stamps.py +0 -0
  106. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/topic_filter.py +0 -0
  107. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/common/xtypes.py +0 -0
  108. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/composite.py +0 -0
  109. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  110. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_cyclone/adapter.py +0 -0
  111. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  112. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/dds_fast/adapter.py +0 -0
  113. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  114. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
  115. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/echo_parser.py +0 -0
  116. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/echo_stream.py +0 -0
  117. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/node_parsers.py +0 -0
  118. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/parsers.py +0 -0
  119. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/process_runner.py +0 -0
  120. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/process_tree.py +0 -0
  121. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_live/sample_extras.py +0 -0
  122. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  123. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/adapter.py +0 -0
  124. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/fixtures.py +0 -0
  125. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/adapters/ros2_mock/node_fixtures.py +0 -0
  126. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/budget.py +0 -0
  127. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/config/__init__.py +0 -0
  128. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/config/settings.py +0 -0
  129. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/constants.py +0 -0
  130. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/__init__.py +0 -0
  131. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/app.py +0 -0
  132. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/http.py +0 -0
  133. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/server/prompts.py +0 -0
  134. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/__init__.py +0 -0
  135. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/bag_service.py +0 -0
  136. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/bag_stats.py +0 -0
  137. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/factory.py +0 -0
  138. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/health.py +0 -0
  139. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/sample_budget.py +0 -0
  140. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/__init__.py +0 -0
  141. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/_common.py +0 -0
  142. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/laser_scan.py +0 -0
  143. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/motion.py +0 -0
  144. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/rate.py +0 -0
  145. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/services/summaries/sensor_data.py +0 -0
  146. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/telemetry/__init__.py +0 -0
  147. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/telemetry/client.py +0 -0
  148. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/tools/__init__.py +0 -0
  149. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/tools/annotations.py +0 -0
  150. {topicforge-0.7.0 → topicforge-0.7.1}/src/topicforge/tools/guard.py +0 -0
  151. {topicforge-0.7.0 → topicforge-0.7.1}/tests/__init__.py +0 -0
  152. {topicforge-0.7.0 → topicforge-0.7.1}/tests/conftest.py +0 -0
  153. {topicforge-0.7.0 → topicforge-0.7.1}/tests/contract/_prompts.json +0 -0
  154. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ground_truth/omnisim_064_results.json +0 -0
  155. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ground_truth/omnisim_064_truth.json +0 -0
  156. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/image_noarr.yaml +0 -0
  157. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/image_trunc4.yaml +0 -0
  158. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_edge.yaml +0 -0
  159. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_noarr.yaml +0 -0
  160. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_trunc128.yaml +0 -0
  161. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/scan_trunc3.yaml +0 -0
  162. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/string_latched.yaml +0 -0
  163. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_echo/twist.yaml +0 -0
  164. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_info_humble.txt +0 -0
  165. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_info_no_param_services.txt +0 -0
  166. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_info_not_found.txt +0 -0
  167. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_duplicates.txt +0 -0
  168. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_empty.txt +0 -0
  169. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_plain.txt +0 -0
  170. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/node_list_warning_stdout.txt +0 -0
  171. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/param_dump_humble.yaml +0 -0
  172. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_node/param_dump_secrets.yaml +0 -0
  173. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_clock.txt +0 -0
  174. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +0 -0
  175. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +0 -0
  176. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_scan.txt +0 -0
  177. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_tf.txt +0 -0
  178. {topicforge-0.7.0 → topicforge-0.7.1}/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +0 -0
  179. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/__init__.py +0 -0
  180. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/Dockerfile +0 -0
  181. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/__init__.py +0 -0
  182. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/blocked_node.py +0 -0
  183. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/entrypoint.sh +0 -0
  184. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/ground_truth.py +0 -0
  185. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/publisher.py +0 -0
  186. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/run_bench.py +0 -0
  187. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/ros2/test_live_adapter.py +0 -0
  188. {topicforge-0.7.0 → topicforge-0.7.1}/tests/integration/test_real_bus.py +0 -0
  189. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_analyze_bag_multi_format.py +0 -0
  190. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_omnisim_humble.py +0 -0
  191. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_service.py +0 -0
  192. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_stats.py +0 -0
  193. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_bag_summaries.py +0 -0
  194. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_cdr_decoder.py +0 -0
  195. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_composite_adapter.py +0 -0
  196. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_config.py +0 -0
  197. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_contract_shape.py +0 -0
  198. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_contract_snapshots.py +0 -0
  199. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_contract_v2.py +0 -0
  200. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_cyclone_adapter.py +0 -0
  201. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_cross_vendor.py +0 -0
  202. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_helpers.py +0 -0
  203. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_inactive_reason.py +0 -0
  204. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_introspection.py +0 -0
  205. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_qos_normalization.py +0 -0
  206. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_dds_schemas.py +0 -0
  207. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_discovery_tracker.py +0 -0
  208. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_echo_parser.py +0 -0
  209. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_echo_stream.py +0 -0
  210. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_endpoints.py +0 -0
  211. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_example_node_spec.py +0 -0
  212. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_factory.py +0 -0
  213. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_fast_adapter.py +0 -0
  214. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_field_names.py +0 -0
  215. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_ground_truth_compare.py +0 -0
  216. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_health.py +0 -0
  217. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_history_and_hints.py +0 -0
  218. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_honest_outputs.py +0 -0
  219. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_http_transport.py +0 -0
  220. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_inspector.py +0 -0
  221. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_lifecycle_buffer.py +0 -0
  222. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_adapter_graph.py +0 -0
  223. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_adapter_parse.py +0 -0
  224. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_adapter_subprocess.py +0 -0
  225. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_live_sample_notes.py +0 -0
  226. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_metrics_buffer.py +0 -0
  227. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_mock_adapter.py +0 -0
  228. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_node_logic.py +0 -0
  229. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_node_parsers.py +0 -0
  230. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_node_tools.py +0 -0
  231. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_peek_bag_samples.py +0 -0
  232. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_process_runner.py +0 -0
  233. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_process_tree.py +0 -0
  234. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_prompts.py +0 -0
  235. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_qos_analyzer.py +0 -0
  236. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_qos_endpoints.py +0 -0
  237. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_qos_scan.py +0 -0
  238. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_rate_verdicts.py +0 -0
  239. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_sample_options.py +0 -0
  240. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_sample_summary_flow.py +0 -0
  241. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_server_guard.py +0 -0
  242. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_server_info_version.py +0 -0
  243. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_server_lanes.py +0 -0
  244. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_stamps.py +0 -0
  245. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_summaries.py +0 -0
  246. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_telemetry.py +0 -0
  247. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_tool_annotations.py +0 -0
  248. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_tool_descriptions.py +0 -0
  249. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_tools_integration.py +0 -0
  250. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_topic_metrics.py +0 -0
  251. {topicforge-0.7.0 → topicforge-0.7.1}/tests/test_version_consistency.py +0 -0
  252. {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.0...HEAD
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.0
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. So far the author has observed Cyclone DDS and Dust DDS participants on a live bus. RTI Connext, OpenDDS, CoreDX and Fast DDS 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).
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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%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.0%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).
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 a real bus, with Cyclone and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`). The Fast DDS adapter 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.
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.0"` | TopicForge server version |
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 changes 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.
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. So far the author has observed Cyclone DDS and Dust DDS participants on a live bus. RTI Connext, OpenDDS, CoreDX and Fast DDS 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).
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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%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.0%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).
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 a real bus, with Cyclone and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`). The Fast DDS adapter 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.
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.0"` | TopicForge server version |
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 changes 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.
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.0" topicforge
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.0`) is moved on every release together with the other
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.0"`, then use
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.0", "topicforge"],
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.0" topicforge
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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%3D)
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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjcuMCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0=`
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.0%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)
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.0%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`
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.0", "topicforge"],
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.0",
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.0"
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.0", "topicforge"],
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.0", "topicforge"]
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.0", topicforge]
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.0" topicforge --version`.
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. Reserved optional fields
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. Reserved optional fields
253
+ ## 3. Optional fields
253
254
 
254
- These shapes are fixed now so that they can be added later without a break. Until a
255
- tool fills them, they do not appear in its output (1.3, mechanism 1). Each is optional
256
- and additive.
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
- Not a tool: an optional list on a diagnostic finding (first on a QoS mismatch report).
261
- TopicForge diagnoses, proposes a fix as text, a human applies it, TopicForge verifies.
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": "qos_overrides./scan.subscription.reliability: best_effort",
268
- "tradeoff": "The reader stops requesting retransmission, so it can miss samples.",
269
- "verify_with": "detect_qos_mismatches"}
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 verb; the list lives in that test.
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). The Fast DDS adapter 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).
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.0
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.0 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.
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