topicforge 0.5.5__tar.gz → 0.6.0__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 (177) hide show
  1. {topicforge-0.5.5 → topicforge-0.6.0}/.gitignore +7 -36
  2. {topicforge-0.5.5 → topicforge-0.6.0}/CHANGELOG.md +230 -200
  3. {topicforge-0.5.5 → topicforge-0.6.0}/PKG-INFO +36 -34
  4. {topicforge-0.5.5 → topicforge-0.6.0}/README.md +32 -31
  5. {topicforge-0.5.5 → topicforge-0.6.0}/docs/DDS_QUICKSTART.md +15 -15
  6. {topicforge-0.5.5 → topicforge-0.6.0}/docs/TESTING.md +5 -5
  7. {topicforge-0.5.5 → topicforge-0.6.0}/docs/TROUBLESHOOTING.md +12 -12
  8. {topicforge-0.5.5 → topicforge-0.6.0}/docs/TUTORIEL.md +16 -20
  9. topicforge-0.6.0/docs/dds-interop-matrix.md +55 -0
  10. {topicforge-0.5.5 → topicforge-0.6.0}/examples/README.md +2 -2
  11. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/00_hello_pub_sub/README.md +17 -23
  12. topicforge-0.6.0/examples/dds/01_who_is_on_the_bus/README.md +47 -0
  13. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/02_why_cant_they_talk/README.md +18 -22
  14. topicforge-0.6.0/examples/dds/03_a_node_crashed/README.md +56 -0
  15. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/04_late_joiner_misses_data/README.md +12 -16
  16. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/05_reliability_in_code/README.md +14 -16
  17. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/06_durability_late_joiner_in_code/README.md +24 -30
  18. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/07_deadline_in_code/README.md +15 -22
  19. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/08_crash_seen_from_inside/README.md +21 -29
  20. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/10_lidar_silent_after_driver_swap/README.md +17 -20
  21. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/11_who_talks_to_whom/README.md +17 -20
  22. topicforge-0.6.0/examples/dds/12_safety_monitor_dropout/README.md +59 -0
  23. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/13_deadline_not_offered/README.md +18 -23
  24. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/14_restart_loop/README.md +14 -18
  25. {topicforge-0.5.5 → topicforge-0.6.0}/examples/dds/README.md +4 -4
  26. {topicforge-0.5.5 → topicforge-0.6.0}/pyproject.toml +5 -4
  27. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/agent_eval/README.md +6 -5
  28. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/README.md +12 -14
  29. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/fast_publisher_cpp/README.md +2 -2
  30. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/fast_py/README.md +3 -3
  31. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/opensplice_publisher/README.md +2 -2
  32. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/__init__.py +1 -1
  33. topicforge-0.6.0/src/topicforge/adapters/base.py +131 -0
  34. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/__init__.py +2 -0
  35. topicforge-0.6.0/src/topicforge/adapters/common/cdr_decoder.py +176 -0
  36. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/dds_helpers.py +65 -77
  37. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/dds_introspection.py +18 -36
  38. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/discovery_tracker.py +10 -16
  39. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/endpoints.py +7 -6
  40. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/lifecycle.py +32 -81
  41. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/metrics_buffer.py +36 -92
  42. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/qos_analyzer.py +18 -9
  43. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/qos_endpoints.py +12 -10
  44. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/qos_normalize.py +63 -50
  45. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/qos_scan.py +61 -23
  46. topicforge-0.6.0/src/topicforge/adapters/common/xtypes.py +97 -0
  47. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/composite.py +28 -26
  48. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/dds_cyclone/adapter.py +109 -222
  49. topicforge-0.6.0/src/topicforge/adapters/dds_dust/__init__.py +10 -0
  50. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/dds_dust/adapter.py +22 -23
  51. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/dds_fast/adapter.py +67 -148
  52. topicforge-0.6.0/src/topicforge/adapters/dds_opendds/__init__.py +11 -0
  53. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/dds_opendds/adapter.py +26 -33
  54. topicforge-0.6.0/src/topicforge/adapters/ros2_live/adapter.py +553 -0
  55. topicforge-0.6.0/src/topicforge/adapters/ros2_live/echo_parser.py +165 -0
  56. topicforge-0.6.0/src/topicforge/adapters/ros2_live/echo_stream.py +195 -0
  57. topicforge-0.6.0/src/topicforge/adapters/ros2_live/parsers.py +113 -0
  58. topicforge-0.6.0/src/topicforge/adapters/ros2_live/process_tree.py +193 -0
  59. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/ros2_mock/adapter.py +25 -22
  60. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/ros2_mock/fixtures.py +67 -107
  61. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/config/settings.py +55 -67
  62. topicforge-0.6.0/src/topicforge/constants.py +37 -0
  63. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/models/schemas.py +178 -86
  64. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/server/app.py +6 -16
  65. topicforge-0.6.0/src/topicforge/services/bag_service.py +412 -0
  66. topicforge-0.6.0/src/topicforge/services/bag_stats.py +218 -0
  67. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/services/factory.py +85 -55
  68. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/services/health.py +19 -21
  69. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/services/inspector.py +90 -56
  70. topicforge-0.6.0/src/topicforge/services/sample_budget.py +53 -0
  71. topicforge-0.6.0/src/topicforge/telemetry/__init__.py +22 -0
  72. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/telemetry/client.py +19 -41
  73. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/tools/handlers.py +229 -151
  74. {topicforge-0.5.5 → topicforge-0.6.0}/tests/conftest.py +16 -0
  75. topicforge-0.6.0/tests/fixtures/ros2_echo/image_noarr.yaml +24 -0
  76. topicforge-0.6.0/tests/fixtures/ros2_echo/image_trunc4.yaml +17 -0
  77. topicforge-0.6.0/tests/fixtures/ros2_echo/scan_edge.yaml +38 -0
  78. topicforge-0.6.0/tests/fixtures/ros2_echo/scan_noarr.yaml +30 -0
  79. topicforge-0.6.0/tests/fixtures/ros2_echo/scan_trunc128.yaml +273 -0
  80. topicforge-0.6.0/tests/fixtures/ros2_echo/scan_trunc3.yaml +46 -0
  81. topicforge-0.6.0/tests/fixtures/ros2_echo/string_latched.yaml +2 -0
  82. topicforge-0.6.0/tests/fixtures/ros2_echo/twist.yaml +18 -0
  83. topicforge-0.6.0/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +20 -0
  84. topicforge-0.6.0/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +104 -0
  85. topicforge-0.6.0/tests/fixtures/ros2_topic_info_verbose_scan.txt +20 -0
  86. topicforge-0.6.0/tests/fixtures/ros2_topic_info_verbose_tf.txt +34 -0
  87. topicforge-0.6.0/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +34 -0
  88. topicforge-0.6.0/tests/integration/ros2/Dockerfile +28 -0
  89. topicforge-0.6.0/tests/integration/ros2/__init__.py +0 -0
  90. topicforge-0.6.0/tests/integration/ros2/entrypoint.sh +43 -0
  91. topicforge-0.6.0/tests/integration/ros2/publisher.py +122 -0
  92. topicforge-0.6.0/tests/integration/ros2/run_bench.py +79 -0
  93. topicforge-0.6.0/tests/integration/ros2/test_live_adapter.py +285 -0
  94. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_analyze_bag_multi_format.py +5 -10
  95. topicforge-0.6.0/tests/test_bag_omnisim_humble.py +150 -0
  96. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_bag_service.py +1 -3
  97. topicforge-0.6.0/tests/test_bag_stats.py +291 -0
  98. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_cdr_decoder.py +34 -13
  99. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_composite_adapter.py +13 -8
  100. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_config.py +7 -7
  101. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_cyclone_adapter.py +44 -7
  102. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_dds_cross_vendor.py +1 -6
  103. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_dds_helpers.py +35 -8
  104. topicforge-0.6.0/tests/test_dds_inactive_reason.py +94 -0
  105. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_dds_introspection.py +4 -5
  106. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_dds_qos_normalization.py +4 -6
  107. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_dds_schemas.py +1 -1
  108. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_discovery_tracker.py +1 -1
  109. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_dust_adapter.py +3 -4
  110. topicforge-0.6.0/tests/test_echo_parser.py +201 -0
  111. topicforge-0.6.0/tests/test_echo_stream.py +247 -0
  112. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_endpoints.py +4 -4
  113. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_factory.py +1 -1
  114. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_fast_adapter.py +3 -3
  115. topicforge-0.6.0/tests/test_history_and_hints.py +273 -0
  116. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_inspector.py +1 -1
  117. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_lifecycle_buffer.py +2 -2
  118. topicforge-0.6.0/tests/test_live_adapter_graph.py +388 -0
  119. topicforge-0.6.0/tests/test_live_adapter_parse.py +103 -0
  120. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_live_adapter_subprocess.py +112 -50
  121. topicforge-0.6.0/tests/test_live_sample_notes.py +150 -0
  122. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_metrics_buffer.py +7 -9
  123. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_mock_adapter.py +35 -10
  124. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_opendds_adapter.py +9 -9
  125. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_peek_bag_samples.py +1 -1
  126. topicforge-0.6.0/tests/test_process_tree.py +209 -0
  127. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_qos_analyzer.py +5 -8
  128. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_qos_endpoints.py +1 -1
  129. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_qos_scan.py +3 -5
  130. topicforge-0.6.0/tests/test_sample_options.py +277 -0
  131. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_telemetry.py +3 -3
  132. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_tools_integration.py +8 -13
  133. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_topic_metrics.py +1 -1
  134. topicforge-0.5.5/docs/dds-interop-matrix.md +0 -58
  135. topicforge-0.5.5/docs/pro.md +0 -38
  136. topicforge-0.5.5/docs/product-plan.md +0 -183
  137. topicforge-0.5.5/examples/dds/01_who_is_on_the_bus/README.md +0 -58
  138. topicforge-0.5.5/examples/dds/03_a_node_crashed/README.md +0 -62
  139. topicforge-0.5.5/examples/dds/12_safety_monitor_dropout/README.md +0 -70
  140. topicforge-0.5.5/src/topicforge/adapters/base.py +0 -143
  141. topicforge-0.5.5/src/topicforge/adapters/common/cdr_decoder.py +0 -203
  142. topicforge-0.5.5/src/topicforge/adapters/common/xtypes.py +0 -120
  143. topicforge-0.5.5/src/topicforge/adapters/dds_dust/__init__.py +0 -12
  144. topicforge-0.5.5/src/topicforge/adapters/dds_opendds/__init__.py +0 -15
  145. topicforge-0.5.5/src/topicforge/adapters/ros2_live/adapter.py +0 -459
  146. topicforge-0.5.5/src/topicforge/constants.py +0 -28
  147. topicforge-0.5.5/src/topicforge/services/bag_service.py +0 -273
  148. topicforge-0.5.5/src/topicforge/telemetry/__init__.py +0 -27
  149. topicforge-0.5.5/tests/fixtures/csv_echo_imu.txt +0 -12
  150. topicforge-0.5.5/tests/fixtures/csv_echo_pose_multi.txt +0 -9
  151. topicforge-0.5.5/tests/test_live_adapter_parse.py +0 -276
  152. {topicforge-0.5.5 → topicforge-0.6.0}/LICENSE +0 -0
  153. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/cyclone_c/README.md +0 -0
  154. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
  155. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
  156. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/dust_py/README.md +0 -0
  157. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/rti_c/README.md +0 -0
  158. {topicforge-0.5.5 → topicforge-0.6.0}/scripts/integration/publishers/rti_cpp/README.md +0 -0
  159. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/__main__.py +0 -0
  160. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/__init__.py +0 -0
  161. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/common/topic_filter.py +0 -0
  162. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  163. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  164. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  165. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  166. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/config/__init__.py +0 -0
  167. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/models/__init__.py +0 -0
  168. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/server/__init__.py +0 -0
  169. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/services/__init__.py +0 -0
  170. {topicforge-0.5.5 → topicforge-0.6.0}/src/topicforge/tools/__init__.py +0 -0
  171. {topicforge-0.5.5 → topicforge-0.6.0}/tests/__init__.py +0 -0
  172. {topicforge-0.5.5 → topicforge-0.6.0}/tests/integration/__init__.py +0 -0
  173. {topicforge-0.5.5 → topicforge-0.6.0}/tests/integration/test_real_bus.py +0 -0
  174. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_example_node_spec.py +0 -0
  175. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_health.py +0 -0
  176. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_honest_outputs.py +0 -0
  177. {topicforge-0.5.5 → topicforge-0.6.0}/tests/test_xtypes.py +0 -0
@@ -160,6 +160,8 @@ dump.rdb
160
160
  *.db3
161
161
  *.mcap
162
162
  rosbag2_*/
163
+ # Test fixtures that are meant to be committed
164
+ !tests/fixtures/bags/**/*.db3
163
165
 
164
166
 
165
167
  # -------------------------------------------------------------------------
@@ -187,42 +189,8 @@ personal/
187
189
  CLAUDE*.md
188
190
 
189
191
 
190
- # -------------------------------------------------------------------------
191
- # Project-local docs and drafts kept out of the package / out of clients.
192
- # Allowlist exceptions: the folder README explains the convention, and
193
- # `*-spec.md` files are committable specs produced by pack-growth streams
194
- # (e.g. Stream C of a release plan). Everything else under projet-file/
195
- # stays local: PDFs, personal market briefs, raw strategy notes.
196
- # -------------------------------------------------------------------------
197
- /docs/projet-file/**
198
- !/docs/projet-file/README.md
199
- !/docs/projet-file/*-spec.md
200
- # Traction snapshots are versioned: weekly JSON + summary + folder README.
201
- # This is the historical curve that informs decision gates G1/G2/G3
202
- # (product-plan section 12). Numeric history must survive across machines.
203
- !/docs/projet-file/traction/
204
- !/docs/projet-file/traction/**
205
- # Launch-post drafts (Reddit, LinkedIn, X). Versioned so the maintainer
206
- # can diff drafts across releases and keep marketing history auditable.
207
- # Drafts, not production copy, never the public README.
208
- !/docs/projet-file/launch-posts/
209
- !/docs/projet-file/launch-posts/**
210
- # Audit reports (security, architecture) + audit-followup triage docs.
211
- # Versioned so audit trails survive across machines and the triage
212
- # decisions are bisectable per release.
213
- !/docs/projet-file/*-audit-*.md
214
- !/docs/projet-file/audit-*.md
215
- # External reference material (OMG interop reports, third-party specs
216
- # snapshots) the maintainer wants pinned in git so strategy decisions
217
- # remain reproducible. Tracked, not part of sdist.
218
- !/docs/projet-file/references/
219
- !/docs/projet-file/references/**
220
- # Archive of superseded strategic artifacts (old audit reports, dated
221
- # launch-post drafts). Kept in git so the historical context survives
222
- # but tucked away so the active `projet-file/` folder stays focused on
223
- # what is currently load-bearing.
224
- !/docs/projet-file/archive/
225
- !/docs/projet-file/archive/**
192
+ # Owner's private strategy notes, specs and traction snapshots: local only.
193
+ /docs/projet-file/
226
194
  /docs/assets/screencast-raw/
227
195
 
228
196
 
@@ -235,3 +203,6 @@ pro/
235
203
  scripts/integration/**/rti_license.dat
236
204
  scripts/integration/**/*.dat
237
205
  .venv-demo/
206
+
207
+ # Owner's traction snapshot script (runs from a local scheduled task): local only.
208
+ /scripts/traction-snapshot.sh
@@ -5,234 +5,262 @@ All notable changes to TopicForge are documented in this file.
5
5
  The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.6.0] - 2026-10-05
9
+
10
+ `sample_messages` (live) rewritten after the OmniSim team's report (count
11
+ ignored, empty results on latched topics, wrong timestamps and shifted columns
12
+ on simulated time).
13
+
14
+ ### Breaking
15
+
16
+ - `sample_messages` payloads are nested named fields (`payload.header.stamp.sec`,
17
+ `payload.ranges`) instead of positional `col_0`, `col_1`, ... columns. The
18
+ verbatim row under `_raw_text` is gone: it is present only when a message could
19
+ not be parsed.
20
+ - `_truncated_after_columns` and `_truncated_columns` are replaced by
21
+ `_truncated_fields`, a list of dotted field paths (`["ranges", "intensities"]`)
22
+ for arrays, strings or bytes cut at `max_array_length`. A cut array no longer
23
+ carries the CLI's `'...'` element.
24
+ - `nan`, `inf` and `-inf` floats are returned as the strings `"nan"`, `"inf"`,
25
+ `"-inf"`.
26
+ - `MiddlewareAdapter.sample_messages` returns a `SampleResult` instead of a list
27
+ of `MessageSample` and takes `timeout_s`. `parse_csv_echo` and `parse_echo_yaml`
28
+ are removed.
29
+ - `pyyaml>=6` is a runtime dependency.
30
+ - A failing `ros2 topic echo` (non-zero exit with no message, or a CLI that cannot
31
+ be started) raises an MCP error instead of returning an empty list. `count` 0 now
32
+ validates the topic in live mode too, as it already did in the mock.
33
+
34
+ ### Added
35
+
36
+ - `sample_messages` parameter `timeout_s` (1..45, default 10): a wall deadline for
37
+ the whole call, topic lookup included. The call returns within about `timeout_s`
38
+ plus 2 s (stopping the CLI, decoding what arrived).
39
+ - `MessageSample.stamp_source` (`header` or `none`) and `received_ns` (wall clock
40
+ when TopicForge read the message).
41
+ - A short result carries a `note` (`N of M messages within T s`) that tells "no
42
+ publisher is announced" from "a publisher exists but nothing arrived in time",
43
+ hints `count` 1 for a latched topic, and gives the exit code and stderr when the
44
+ CLI exited early.
45
+ - A message over the per-message size cap (`TOPICFORGE_MAX_SAMPLE_BYTES`, 1 MiB) is
46
+ dropped while it streams and counted in the `note`, so a large `Image` no longer
47
+ costs tens of seconds of YAML decoding. Decoding uses libyaml when available and
48
+ stops at the call deadline.
49
+ - `count` above 50 is capped with a note, and the tool schema declares the
50
+ maximum.
51
+
52
+ ### Fixed
53
+
54
+ - `sample_messages` honours `count`: it streams `ros2 topic echo` and stops at
55
+ `count` messages or the deadline, then stops the process tree (SIGINT then
56
+ SIGKILL on POSIX, `taskkill /F /T` on Windows) (OmniSim D3). The echo is
57
+ started with explicit `--qos-reliability` and `--qos-durability` taken from the
58
+ publishers, so a latched (`transient_local`) topic is no longer returned empty
59
+ because the CLI picked its QoS against a cold daemon.
60
+ - Stopping the CLI kills the whole process tree even when the launcher already
61
+ exited (POSIX: always signals the process group; Windows: a Job Object), so no
62
+ orphan `ros2` process keeps running. The CLI is started with UTF-8 output.
63
+ - The mock `sample_messages` matches the live shape: headerless messages have
64
+ `timestamp_ns` 0 and `stamp_source` `none`.
65
+ - `timestamp_ns` is the top-level `header.stamp` whatever its value, so a
66
+ simulated clock (seconds since the simulation start) no longer falls back to 0
67
+ and shifts every following column by two (OmniSim D4).
68
+
69
+ ## [0.5.6] - 2026-10-02
70
+
71
+ Fixes from a live run of 0.5.3 and 0.5.5 against OmniSim's simulated
72
+ Clearpath Husky (ROS 2 Humble, Fast DDS, Cyclone backend for the DDS tools),
73
+ reported with ground truth by the OmniSim team.
74
+
75
+ ### Added
76
+
77
+ - `sample_messages` takes `max_array_length` (1..65536, default 128, null for no
78
+ cut) and `arrays_summary_only`. A cut is listed under `_truncated_after_columns`
79
+ in the sample and in `note`.
80
+ - Size caps on returned samples: 1 MiB per message and 4 MiB per call
81
+ (`TOPICFORGE_MAX_SAMPLE_BYTES` sets the per-message cap). Over-cap messages are
82
+ dropped and `note` says so. `peek_bag_samples` is capped the same way, and its
83
+ arrays are cut at 4096 elements.
84
+ - `BagTopicStats` gains `first_timestamp_ns`, `last_timestamp_ns`,
85
+ `frequency_basis` (`topic_span` or `bag_duration`) and `latched`.
86
+ - `TopicInfo.qos_durability`; `qos_reliability` and `qos_durability` are filled
87
+ by `get_topic_info` from the publishers' QoS (`mixed` when they disagree).
88
+ - `HealthReport.dds_inactive_reason` says why `dds_backend` is `none`.
89
+ - A real Humble rosbag2 bag from the OmniSim team as a test fixture
90
+ (`tests/fixtures/bags/omnisim_humble/`).
91
+
92
+ ### Changed
93
+
94
+ - `list_topics` (live) uses one `ros2 topic list -v` call for publisher and
95
+ subscriber counts instead of one `ros2 topic info` per topic, falling back to
96
+ the per-topic calls if the output is not recognized. It leaves QoS null.
97
+ - `rosbags>=0.11.3` is required (0.10 reads a bare `.db3` as ROS 1 and returns
98
+ message definitions and QoS as plain strings).
99
+ - A latched topic gets no rate only when its messages span under 1 second (a
100
+ start-up burst such as `/tf_static`); a latched topic published over a longer
101
+ span keeps its rate. `latched` is unchanged.
102
+ - `analyze_bag` reads per-topic times of an `.mcap` bag only up to 200 MiB and
103
+ 5 s; past either it keeps `bag_duration` rates and says so in the new
104
+ `BagAnalysis.note`.
105
+ - The `policies_checked` entry for History reads "History (risky only, where
106
+ announced)".
107
+ - `max_array_length` also cuts strings and bytes to that many characters plus
108
+ `...`; those cells are listed under `_truncated_columns`.
109
+ - `QosProfile.history` is now optional, and a new `history_note` explains why it
110
+ is missing. DDS discovery does not carry History (the builtin endpoint data has
111
+ no such member), so TopicForge reports it only for its own endpoints and for
112
+ Cyclone DDS peers that set something other than the default. For Fast DDS, RTI
113
+ and unknown-vendor endpoints it is `null`; for a Cyclone peer, KEEP_LAST depth 1
114
+ is also `null` because the binding fills missing QoS with that default. Clients
115
+ that assumed `history` is always a string must handle `null`.
116
+ - A discovered endpoint that announced no History keeps its reliability,
117
+ durability and the other policies; before, the whole `qos` became `null`.
118
+ - `detect_qos_mismatches` judges the KEEP_ALL-vs-KEEP_LAST History risk only
119
+ where both sides announced History; elsewhere one hint states that discovery
120
+ does not carry it, instead of a warning on every pair.
121
+
122
+ ### Fixed
123
+
124
+ - `sample_messages` with `arrays_summary_only` shifted every CSV column after an
125
+ array: `<sequence type: float, length: 541>` contains a comma and was split in
126
+ two. It is one cell now.
127
+ - `sample_messages` returned no samples and no explanation when the echo timed
128
+ out (for example a large message with `max_array_length` null); `note` now says
129
+ so.
130
+ - `list_topics` reported 0 publishers and 0 subscribers for a topic missing from
131
+ `ros2 topic list -v`; it now asks `ros2 topic info` for that topic.
132
+ - `peek_bag_samples` converted a whole numpy array to a list before cutting it
133
+ at 4096 elements; it now converts only the part it keeps.
134
+ - Fast DDS endpoints no longer report a History taken from the binding's
135
+ defaults: `detect_qos_mismatches` treats it as not announced, like the other
136
+ vendors. This path has never run against a real Fast DDS bus.
137
+ - The `tests/fixtures/bags` bag is left out of the sdist.
138
+ - `peek_bag_samples` failed on every Humble `.db3` bag with "Bag contains no
139
+ type definitions". The reader now gets the type definitions of the distro the
140
+ bag records, or Humble, and `note` says which. `LaserScan.ranges` and other
141
+ numeric arrays now come back as lists, not a numpy repr string.
142
+ - `analyze_bag` rates were count / whole-bag duration, 0.1 to 0.7 percent off on
143
+ periodic topics and meaningless on latched ones (`/tf_static` showed 0.06 Hz,
144
+ `/rosout` 0.37 Hz). They are now `(n - 1) / (last - first)` per topic, read
145
+ from the bag when it is readable locally, and latched topics are flagged.
146
+ - `get_topic_info` returned `qos_reliability: null` on every topic although the
147
+ CLI prints Reliability and Durability per endpoint.
148
+ - `peek_dds_samples('/scan')` reported the topic as not discovered while
149
+ `rt/scan` and `scan` worked; it now resolves the name like the other DDS tools
150
+ and says which topic matched.
151
+ - The "DDS module is not active" error said to install the Cyclone binding even
152
+ when it was installed. It now states the actual cause: backend not selected,
153
+ binding missing, or adapter failed to start. README wording aligned.
154
+ - CSV `...` truncation cells from `ros2 topic echo` no longer count as data
155
+ columns.
156
+ - `ros2` output is decoded as UTF-8 on Windows instead of cp1252.
157
+ - `detect_qos_mismatches` no longer calls service, action, `rosout`,
158
+ `parameter_events` or `ros_discovery_info` topics typos of each other (for
159
+ example `get_parametersRequest` vs `set_parametersRequest`), and no longer
160
+ reports them as orphans. Only plain `rt/` topics and bare DDS names are compared.
161
+ - The "differs by N edits" hint is now only given between a writer-only name and
162
+ a reader-only name; a topic with both sides is never suggested as a typo.
9
163
 
10
164
  ## [0.5.5] - 2026-10-02
11
165
 
12
- Driven by a blind evaluation: agents given only TopicForge's tools had to
13
- diagnose live DDS buses with planted faults. The first rounds (on 0.5.4) found
14
- wrong blame on QoS, missed Liveliness and Ownership faults, hand-joined GUIDs
15
- and empty results read as "healthy". After the changes below, 16 of 16
16
- scenarios were diagnosed correctly, with no false alarm on a healthy bus.
166
+ Tool outputs were reworked after testing them with LLM agents on the author's
167
+ 16 test scenarios (live DDS buses with planted faults).
168
+
169
+ ### Breaking
170
+
171
+ - `detect_qos_mismatches` returns a `MismatchScan` envelope instead of a bare
172
+ list. Migrate by reading `["reports"]` where you used the list.
17
173
 
18
174
  ### Added
19
175
 
20
- - **`list_endpoints`, the 12th MCP tool.** Returns every announced DDS writer
21
- and reader as a typed `EndpointInfo` (role, topic, type, owning participant
22
- guid, name and vendor, structured QoS, announcement timestamp) plus a
23
- `by_topic` roll-up that flags orphans (`no_reader`, `no_writer`). TopicForge's
24
- own endpoints are excluded unless `include_observer`, and counted in
25
- `excluded_observer_endpoints`. Endpoints of a participant that left are kept
26
- (200 entries, 1 hour) and shown as `departed_writers` / `departed_readers`;
27
- `include_departed` lists them. Served by Cyclone and the mock. Fast raises a
28
- clear "not supported yet" error.
29
- - **Continuous discovery tracking on Cyclone.** A daemon thread (0.5 s period)
30
- is the only code that reads the builtin discovery topics and feeds in-memory
31
- caches that every discovery tool reads. Lifecycle no longer moves only when a
32
- tool is called: a node restarted three times shows as 3 `lost` and 4
33
- `discovered`, dated by DDS. The 2 s warm-up sleep is gone.
34
- - Timestamps on `participant_events` and `list_participants`: `announced_ns`,
35
- `lost_ns`, `lost_time_source`, and on events `time_source` and `observed_ns`.
36
- A DDS source timestamp and a local observation are never mixed.
37
- - `health_check` gains `now_ns`, `observer_started_ns`, `observed_domain_note`
38
- and the tracker status (`tracker_running`, `tracker_passes`, `tracker_errors`,
39
- `tracker_last_pass_ns`).
40
- - `QosProfile` gains optional `liveliness_kind`, `liveliness_lease_ns`,
41
- `ownership_kind`, `ownership_strength`, `partitions`, `latency_budget_ns`,
42
- `destination_order` and `data_representation`, read from Cyclone discovery.
43
- A missing Partition policy is reported as `[""]` everywhere.
44
- - `peek_dds_samples` on `DCPSPublication`, `DCPSSubscription` and
45
- `DCPSParticipant` carries structured `role`, `participant_guid`,
46
- `participant_name`, `type_id`, `qos`, `announced_ns` and `is_observer`, and
47
- sets `timestamp_ns` from the announcement. `_raw_text` is kept only for a
48
- sample with nothing structured to read, truncated to 300 characters.
49
- - `peek_dds_samples` on a builtin topic returns the cached discovery state, not
50
- a stream.
51
- - Topic filters accept `rt/x` and `x` interchangeably and report which form
52
- matched. A filter that matches nothing returns the list of known topics.
53
- - Examples: the generic role nodes take partition, liveliness and ownership
54
- options.
176
+ - `list_endpoints`, the twelfth tool: every announced DDS writer and reader with
177
+ owning participant, structured QoS and a per-topic roll-up that flags orphans
178
+ (`no_reader`, `no_writer`). Cyclone and mock only.
179
+ - Continuous discovery tracking on Cyclone, so a node restarted three times
180
+ shows as 3 `lost` and 4 `discovered` events dated by DDS, not by poll time.
181
+ - `announced_ns`, `lost_ns`, `time_source` and related timestamp fields on
182
+ `participant_events` and `list_participants`.
183
+ - `health_check` reports `now_ns`, tracker status and an observed-domain note.
184
+ - `QosProfile` gains Liveliness, Ownership, Partition, LatencyBudget,
185
+ DestinationOrder and DataRepresentation.
186
+ - `peek_dds_samples` on builtin discovery topics returns structured endpoint
187
+ and participant fields instead of a raw repr string.
188
+ - Topic filters accept `rt/x` and `x` interchangeably; a filter that matches
189
+ nothing returns the known topics.
190
+ - `detect_qos_mismatches` also reports `matched` and `not_matched` pairs, hints
191
+ (near-miss topic names, path suffixes, type id differences), and the policies
192
+ it did and did not check.
55
193
 
56
194
  ### Changed
57
195
 
58
- - **Breaking: `detect_qos_mismatches` returns a `MismatchScan` envelope instead
59
- of a bare list.** To migrate, read `["reports"]` where you used the list. The
60
- envelope also carries `matched`, `not_matched`, `hints`, `pairs_checked`,
61
- `topics_scanned`, `policies_checked`, `policies_unchecked` and
62
- `mode_effective`. Why: an empty list was read as a healthy bus although only
63
- four policies were checked, and readers and writers in different partitions
64
- were blamed on Reliability.
65
- - Partition is checked first (`*` and `?` wildcards; a wildcard against a
66
- wildcard never matches). A pair it separates is a `not_matched` entry with
67
- reason `partition`; differing type names give reason `type_name`. The RxO
68
- rules are not run on either. A `not_matched` pair lists the policies that
69
- would be incompatible if it matched (`latent_incompatible_policies`).
70
- - Liveliness (kind and lease), LatencyBudget, Ownership (kind equality),
196
+ - `detect_qos_mismatches` checks Partition first (with `*` and `?` wildcards),
197
+ then type names, then the RxO rules; Liveliness, LatencyBudget, Ownership,
71
198
  DestinationOrder and DataRepresentation join Reliability, Durability and
72
- Deadline. History stays a labelled `risky` finding. A policy a side did not
73
- announce is skipped, never guessed.
74
- - `MismatchReport` gains, additively, the reader and writer participant guid and
75
- name, type names, `details` (requested and offered value, failed rule) and
76
- `unchecked`.
77
- - Hints cover orphan topics whose names differ by at most 2 edits (compared
78
- against all topics), path-suffix matches and XTypes type id differences (a
79
- note, never a mismatch). Hints are prioritized and say how many were omitted.
80
- - A late joiner is not a hint (it is normal on most buses): a `MatchedPair` is
81
- flagged `late_joiner` when its writer is VOLATILE and the reader, on the same
82
- host, appeared more than 1 s later.
83
- - `reports`, `matched` and `not_matched` are capped at 200 entries each, with
84
- `reports_total`, `matched_total`, `not_matched_total` and `truncated`.
85
- - `topic_metrics` reports a `status`, and on a user topic says it has no data
86
- instead of returning zeros. `peek_dds_samples` on a user topic returns count
87
- 0 and a note. `ParticipantInfo` and endpoints gain `is_observer`, and
88
- `vendor_source` says where a vendor came from.
89
- - Tool descriptions no longer carry internal history, and state the
90
- single-domain and EXCLUSIVE-ownership facts.
91
- - `EndpointInfo.activity` is reserved (always `None`) with an `activity_note`
92
- saying liveness is not observed.
199
+ Deadline.
200
+ - `topic_metrics` and `peek_dds_samples` on a user topic say they have no data
201
+ instead of returning zeros or a placeholder.
202
+ - Tool descriptions no longer carry internal history.
93
203
 
94
204
  ### Fixed
95
205
 
96
- - Infinite durations (cyclonedds reports 9223372036854775807) are normalized
97
- to `None`; an infinite Deadline used to surface as a 9.2e18 ns deadline.
98
- - The Cyclone discovery tracker is stopped at interpreter exit.
99
- - A race between the tracker and a tool call could mark a live participant as
100
- lost for good; a failed read could drop a participant's departure; endpoints
101
- of a participant that had just left could stay listed as live. The tracker
102
- and every tool now share one lock, and taken samples are never discarded.
103
- - The cyclonedds Python binding is not thread-safe when it converts QoS: two
104
- threads in `take()` at once corrupted the heap on Windows. Every binding call
105
- now goes through one process-wide lock, and tool handlers no longer call the
106
- binding at all.
107
- - If every tracker pass fails, tools no longer wait 3 s each: warm-up is bounded
108
- once. `health_check` reports failed passes and cache evictions.
109
- - An unreadable QoS duration is treated as unknown (`QosProfile.unknown_policies`),
110
- not as infinite, so it cannot produce a false Deadline incompatibility.
111
- - Topic-name typo hints are bounded (banded edit distance, orphan cap, call
112
- budget), so a bus with a thousand topics does not stall the call.
113
- - Partition matching was checked against a live Cyclone bus: `*` and `?` are
114
- wildcards, `[...]` is literal, and two wildcard expressions never match each
115
- other. Pinned by tests.
206
+ - Infinite durations are `None` instead of 9223372036854775807.
207
+ - Races between the tracker and tool calls could mark a live participant lost
208
+ or keep endpoints of a departed one.
209
+ - Concurrent cyclonedds calls could corrupt the heap on Windows; every binding
210
+ call now takes one lock.
211
+ - A failing tracker no longer adds 3 s to every tool call.
212
+ - An unreadable QoS duration is treated as unknown, not infinite.
213
+ - Typo hints stay bounded on a bus with a thousand topics.
116
214
 
117
215
  ### Known limits
118
216
 
119
- - A hung writer (alive, lease renewed, no data) is not observable without a
120
- data probe. An opt-in probe is planned for 0.5.6.
121
- - A crash and a clean leave cannot be told apart, and `lost_ns` is an upper
122
- bound of the death (the lease expiry after a crash).
123
- - A participant that cycles faster than the discovery history depth between two
124
- tracker passes can be missed.
125
- - The Fast DDS backend has never run on a bus, and `list_endpoints` is not
217
+ - A hung writer (alive, no data) cannot be observed without a data probe.
218
+ - A crash and a clean leave look the same; `lost_ns` is an upper bound.
219
+ - A participant that cycles faster than the history depth between two tracker
220
+ passes can be missed.
221
+ - The Fast DDS backend has never run on a bus; `list_endpoints` is not
126
222
  supported there.
127
- - DDS Security is not supported.
128
- - User-topic payload decoding is disabled, so `topic_metrics` has no data on
129
- user topics.
223
+ - DDS Security is not supported, and user-topic payload decoding is disabled.
130
224
 
131
225
  ## [0.5.4] - 2026-10-02
132
226
 
133
- First run of the DDS code against a live multi-vendor bus (Windows 11, a
134
- Python / Cyclone DDS participant and a Rust / Dust DDS participant, TopicForge
135
- driven by a real MCP client). Until now every DDS adapter had only been
136
- checked statically. The run exposed four defects that together made the DDS
137
- module non-functional on Cyclone; all are fixed and pinned by tests.
227
+ First run of the DDS code against a live bus (Windows 11, a Python / Cyclone DDS
228
+ participant and a Rust / Dust DDS participant). It exposed four defects that
229
+ left the DDS module non-functional on Cyclone; all are fixed and tested.
138
230
 
139
231
  ### Fixed
140
232
 
141
- - Role nodes published below their rate (about 7 Hz for 10 Hz on Windows):
142
- they now keep an absolute schedule.
143
-
144
- - **`list_participants` now reports the participant name and the hostname on
145
- Cyclone.** The name comes from the EntityName QoS and the hostname from the
146
- `__Hostname` discovery property; both were always null on a real bus.
147
- - **Participant GUIDs were never read.** cyclonedds 11.0.1 exposes the builtin
148
- key as a `uuid.UUID`, which the extractor did not handle, so every
149
- participant collapsed onto a single `unknown` entry.
150
- - **Vendors were never identified.** The builtin participant sample carries no
151
- vendor field. The vendor id is now read from the first two bytes of the GUID
152
- prefix, as RTPS recommends; implementations that do not follow that
153
- convention (Dust DDS, and RTI by default) still report `unknown`, because
154
- the Cyclone Python binding does not expose the vendor id from the RTPS
155
- header.
156
- - **The OMG vendor-id table was wrong.** It mapped `01.05` to Fast DDS and
157
- `01.16` to Cyclone; the correct ids are `01.0F` (eProsima) and `01.10`
158
- (Eclipse), verified against both vendors' sources. The Cyclone side ran on a
159
- live bus; the Fast DDS side (`fast_extract_vendor_id`) is verified
160
- statically only, since its tests need a binding that is not on PyPI. The `vendor` field of
161
- `ParticipantInfo` and `ParticipantEvent` now also accepts `rti_micro`,
162
- `opensplice`, `opendds`, `coredx`, `intercom` and `dust` (soft-breaking for
163
- clients validating the previous enum).
164
- - **`detect_qos_mismatches` never reported anything on Cyclone.** cyclonedds
165
- scopes its policy class names (`Reliability.BestEffort`), and the
166
- normalizer matched only the bare name, so no QoS profile was ever built.
167
- - **A stopped participant never disappeared.** Discovery readers keep the last
168
- sample of a departed participant with a NOT_ALIVE instance state; those are
169
- now ignored for participants and endpoints, so a participant is reported as
170
- left when its lease expires, and a dead endpoint no longer produces a
171
- mismatch.
172
-
173
- - Cyclone adapter created a new DDS reader on a builtin discovery topic on
174
- every tool call and never deleted it. It now keeps one reader per builtin
175
- topic and takes a non-blocking snapshot, so calls no longer wait 2 s each
176
- (`detect_qos_mismatches` waited 4 s).
177
- - `peek_dds_samples` on `DCPSPublication` / `DCPSSubscription` reported the
178
- endpoints of participants that had left; disposed entries are now dropped.
179
- Its payload also carries the endpoint `type_name`.
180
- - `test_dds_cross_vendor.py` expected an error message from v0.3; it had
181
- never run, since CI has no DDS binding. First run against the real Cyclone
182
- binding.
233
+ - `list_participants` reports the participant name and hostname on Cyclone
234
+ (both were always null).
235
+ - Participant GUIDs were never read, so every participant collapsed onto one
236
+ `unknown` entry.
237
+ - Vendors were never identified. The vendor id is now read from the GUID prefix;
238
+ Dust DDS and RTI by default still report `unknown`.
239
+ - The OMG vendor-id table mapped Fast DDS and Cyclone to the wrong ids (now
240
+ `01.0F` and `01.10`). The Fast DDS side is verified statically only.
241
+ - `ParticipantInfo.vendor` also accepts `rti_micro`, `opensplice`, `opendds`,
242
+ `coredx`, `intercom` and `dust`.
243
+ - `detect_qos_mismatches` never reported anything on Cyclone because policy
244
+ class names were not normalized.
245
+ - A stopped participant never disappeared; departed participants and endpoints
246
+ are now dropped.
247
+ - The Cyclone adapter leaked a reader per tool call and waited 2 s each; it now
248
+ keeps one reader per builtin topic.
249
+ - `peek_dds_samples` on `DCPSPublication` / `DCPSSubscription` listed endpoints
250
+ of participants that had left, and now includes the endpoint `type_name`.
251
+ - Example role nodes published below their rate on Windows.
183
252
 
184
253
  ### Added
185
254
 
186
- - `examples/dds/`: a "write the code" track. Examples 00 (hello publisher
187
- and subscriber), 05 (Reliability), 06 (Durability and the late joiner),
188
- 07 (Deadline declared and kept) and 08 (a crash seen from inside and from
189
- outside) each ship a readable `publisher.py` and `subscriber.py` in Cyclone
190
- DDS Python; the subscriber prints what it receives and the DDS statuses,
191
- and TopicForge explains the same situation from outside. All fourteen
192
- examples pass on a live bus.
193
- - The generic role nodes print one line per second per reader with what
194
- they received, and examples 02, 04, 10 and 13 check that the broken
195
- subscriber receives nothing while the control one receives data.
196
- - The harness keeps each program's output in a log file and prints it live
197
- under `--hold`.
198
-
199
- - `scripts/integration/interop_check.py`: one-command multi-vendor demo
200
- that starts a Rust / Dust and a Python / Cyclone participant, drives
201
- TopicForge over stdio through the official MCP client, checks participant
202
- discovery, a deliberate Reliability mismatch between the two vendors, and
203
- the departure of a stopped participant, then stops every process it started.
204
- - A real Rust / Dust DDS participant (`publishers/dust_publisher`) and a real
205
- Python / Cyclone participant replacing the previous scaffold, which never
206
- wrote a sample.
207
- - Twelve interop programs in total, one per vendor and language with an
208
- officially released binding (Cyclone C / C++ / Rust / Python, Dust Rust /
209
- Python, Fast DDS C++ / Python, RTI Connext C / C++ / Python, OpenSplice C),
210
- all following the contract in `scripts/integration/DEMO_CONTRACT.md`. The
211
- driver starts whichever ones are built on the host and adapts its checks;
212
- `--list` shows what can run. Only Cyclone Python and Dust Rust / Python / C
213
- have been run, on Windows; the rest are written but unrun. RTI participants
214
- need a local license and are never run in CI.
215
- - One-command launch scripts, `scripts/integration/launch/setup` and
216
- `run_demo` (`.ps1` and `.sh`), which create `.venv-demo`, install the Cyclone
217
- binding and build the Rust participant. `setup.ps1 -Firewall` adds inbound
218
- UDP 7400-7500 rules on private networks for multi-machine runs.
219
- - Unicast peer configuration for Cyclone and Fast DDS and a guide for a mixed
220
- Linux and Windows bus (`scripts/integration/config/`).
221
- - `.github/workflows/demo.yml` runs the driver with the Cyclone and Dust
222
- participants on Ubuntu and Windows; `demo-fast.yml` builds Fast DDS 3 from
223
- pinned tags and starts the C++ participant (weekly and manual).
224
- - `scripts/integration/README.md` rewritten around the demo: it previously
225
- described the removed docker / scenario rig.
226
-
227
- - `examples/dds/`: real use cases 10 to 14 (driver swap, wiring, safety
228
- monitor dropout, deadline not offered, restart loop) next to the concept
229
- examples 01 to 04. All nine pass on a live bus.
255
+ - `examples/dds/`: fourteen runnable examples (concepts and use cases), all
256
+ passing on a live bus.
257
+ - `scripts/integration/`: a demo driver, twelve interop programs (Cyclone, Dust,
258
+ Fast DDS, RTI, OpenSplice), one-command setup scripts and CI workflows. Only
259
+ Cyclone Python and Dust Rust / Python / C have been run, on Windows.
230
260
 
231
261
  ### Removed
232
262
 
233
- - The docker / scenario integration rig (`scenarios_runner.py`, `run-local.*`,
234
- `docker-compose.yml`, per-vendor Dockerfiles, scenario JSON files, schema
235
- test and `integration.yml`) is removed in favour of the demo driver.
263
+ - The docker / scenario integration rig, replaced by the demo driver.
236
264
 
237
265
  ## [0.5.3] - 2026-10-01
238
266
 
@@ -1235,7 +1263,9 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
1235
1263
  - The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
1236
1264
  - `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
1237
1265
 
1238
- [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...HEAD
1266
+ [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.0...HEAD
1267
+ [0.6.0]: https://github.com/yaniswav/TopicForge/compare/v0.5.6...v0.6.0
1268
+ [0.5.6]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...v0.5.6
1239
1269
  [0.5.5]: https://github.com/yaniswav/TopicForge/compare/v0.5.4...v0.5.5
1240
1270
  [0.5.4]: https://github.com/yaniswav/TopicForge/compare/v0.5.3...v0.5.4
1241
1271
  [0.5.3]: https://github.com/yaniswav/TopicForge/compare/v0.5.2...v0.5.3