topicforge 0.5.5__tar.gz → 0.5.6__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 (160) hide show
  1. {topicforge-0.5.5 → topicforge-0.5.6}/.gitignore +7 -36
  2. {topicforge-0.5.5 → topicforge-0.5.6}/CHANGELOG.md +168 -200
  3. {topicforge-0.5.5 → topicforge-0.5.6}/PKG-INFO +34 -33
  4. {topicforge-0.5.5 → topicforge-0.5.6}/README.md +31 -30
  5. {topicforge-0.5.5 → topicforge-0.5.6}/docs/DDS_QUICKSTART.md +15 -15
  6. {topicforge-0.5.5 → topicforge-0.5.6}/docs/TESTING.md +5 -5
  7. {topicforge-0.5.5 → topicforge-0.5.6}/docs/TROUBLESHOOTING.md +11 -11
  8. {topicforge-0.5.5 → topicforge-0.5.6}/docs/TUTORIEL.md +16 -20
  9. topicforge-0.5.6/docs/dds-interop-matrix.md +55 -0
  10. {topicforge-0.5.5 → topicforge-0.5.6}/examples/README.md +2 -2
  11. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/00_hello_pub_sub/README.md +17 -23
  12. topicforge-0.5.6/examples/dds/01_who_is_on_the_bus/README.md +47 -0
  13. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/02_why_cant_they_talk/README.md +18 -22
  14. topicforge-0.5.6/examples/dds/03_a_node_crashed/README.md +56 -0
  15. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/04_late_joiner_misses_data/README.md +12 -16
  16. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/05_reliability_in_code/README.md +14 -16
  17. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/06_durability_late_joiner_in_code/README.md +24 -30
  18. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/07_deadline_in_code/README.md +15 -22
  19. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/08_crash_seen_from_inside/README.md +21 -29
  20. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/10_lidar_silent_after_driver_swap/README.md +17 -20
  21. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/11_who_talks_to_whom/README.md +17 -20
  22. topicforge-0.5.6/examples/dds/12_safety_monitor_dropout/README.md +59 -0
  23. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/13_deadline_not_offered/README.md +18 -23
  24. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/14_restart_loop/README.md +14 -18
  25. {topicforge-0.5.5 → topicforge-0.5.6}/examples/dds/README.md +4 -4
  26. {topicforge-0.5.5 → topicforge-0.5.6}/pyproject.toml +4 -4
  27. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/agent_eval/README.md +6 -5
  28. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/README.md +12 -14
  29. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/fast_publisher_cpp/README.md +2 -2
  30. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/fast_py/README.md +3 -3
  31. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/opensplice_publisher/README.md +2 -2
  32. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/__init__.py +1 -1
  33. topicforge-0.5.6/src/topicforge/adapters/base.py +131 -0
  34. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/__init__.py +2 -0
  35. topicforge-0.5.6/src/topicforge/adapters/common/cdr_decoder.py +176 -0
  36. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/dds_helpers.py +65 -77
  37. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/dds_introspection.py +18 -36
  38. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/discovery_tracker.py +10 -16
  39. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/endpoints.py +7 -6
  40. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/lifecycle.py +32 -81
  41. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/metrics_buffer.py +36 -92
  42. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/qos_analyzer.py +18 -9
  43. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/qos_endpoints.py +12 -10
  44. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/qos_normalize.py +63 -50
  45. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/qos_scan.py +61 -23
  46. topicforge-0.5.6/src/topicforge/adapters/common/xtypes.py +97 -0
  47. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/composite.py +26 -25
  48. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/dds_cyclone/adapter.py +108 -222
  49. topicforge-0.5.6/src/topicforge/adapters/dds_dust/__init__.py +10 -0
  50. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/dds_dust/adapter.py +21 -22
  51. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/dds_fast/adapter.py +66 -148
  52. topicforge-0.5.6/src/topicforge/adapters/dds_opendds/__init__.py +11 -0
  53. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/dds_opendds/adapter.py +25 -32
  54. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/ros2_live/adapter.py +194 -137
  55. topicforge-0.5.6/src/topicforge/adapters/ros2_live/parsers.py +113 -0
  56. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/ros2_mock/adapter.py +17 -20
  57. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/ros2_mock/fixtures.py +48 -99
  58. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/config/settings.py +55 -67
  59. topicforge-0.5.6/src/topicforge/constants.py +25 -0
  60. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/models/schemas.py +141 -67
  61. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/server/app.py +6 -16
  62. topicforge-0.5.6/src/topicforge/services/bag_service.py +412 -0
  63. topicforge-0.5.6/src/topicforge/services/bag_stats.py +218 -0
  64. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/services/factory.py +83 -55
  65. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/services/health.py +19 -21
  66. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/services/inspector.py +81 -51
  67. topicforge-0.5.6/src/topicforge/services/sample_budget.py +53 -0
  68. topicforge-0.5.6/src/topicforge/telemetry/__init__.py +22 -0
  69. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/telemetry/client.py +19 -41
  70. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/tools/handlers.py +180 -142
  71. {topicforge-0.5.5 → topicforge-0.5.6}/tests/conftest.py +16 -0
  72. topicforge-0.5.6/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +20 -0
  73. topicforge-0.5.6/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +104 -0
  74. topicforge-0.5.6/tests/fixtures/ros2_topic_info_verbose_scan.txt +20 -0
  75. topicforge-0.5.6/tests/fixtures/ros2_topic_info_verbose_tf.txt +34 -0
  76. topicforge-0.5.6/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +34 -0
  77. topicforge-0.5.6/tests/integration/ros2/Dockerfile +28 -0
  78. topicforge-0.5.6/tests/integration/ros2/__init__.py +0 -0
  79. topicforge-0.5.6/tests/integration/ros2/entrypoint.sh +43 -0
  80. topicforge-0.5.6/tests/integration/ros2/publisher.py +112 -0
  81. topicforge-0.5.6/tests/integration/ros2/run_bench.py +79 -0
  82. topicforge-0.5.6/tests/integration/ros2/test_live_adapter.py +191 -0
  83. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_analyze_bag_multi_format.py +5 -10
  84. topicforge-0.5.6/tests/test_bag_omnisim_humble.py +150 -0
  85. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_bag_service.py +1 -3
  86. topicforge-0.5.6/tests/test_bag_stats.py +291 -0
  87. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_cdr_decoder.py +34 -13
  88. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_composite_adapter.py +8 -6
  89. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_config.py +7 -7
  90. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_cyclone_adapter.py +44 -7
  91. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_dds_cross_vendor.py +1 -6
  92. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_dds_helpers.py +35 -8
  93. topicforge-0.5.6/tests/test_dds_inactive_reason.py +94 -0
  94. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_dds_introspection.py +4 -5
  95. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_dds_qos_normalization.py +4 -6
  96. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_dds_schemas.py +1 -1
  97. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_discovery_tracker.py +1 -1
  98. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_dust_adapter.py +3 -4
  99. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_endpoints.py +4 -4
  100. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_factory.py +1 -1
  101. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_fast_adapter.py +3 -3
  102. topicforge-0.5.6/tests/test_history_and_hints.py +273 -0
  103. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_inspector.py +1 -1
  104. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_lifecycle_buffer.py +2 -2
  105. topicforge-0.5.6/tests/test_live_adapter_graph.py +404 -0
  106. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_metrics_buffer.py +7 -9
  107. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_mock_adapter.py +7 -8
  108. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_opendds_adapter.py +9 -9
  109. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_peek_bag_samples.py +1 -1
  110. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_qos_analyzer.py +5 -8
  111. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_qos_endpoints.py +1 -1
  112. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_qos_scan.py +3 -5
  113. topicforge-0.5.6/tests/test_sample_options.py +217 -0
  114. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_telemetry.py +3 -3
  115. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_tools_integration.py +8 -13
  116. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_topic_metrics.py +1 -1
  117. topicforge-0.5.5/docs/dds-interop-matrix.md +0 -58
  118. topicforge-0.5.5/docs/pro.md +0 -38
  119. topicforge-0.5.5/docs/product-plan.md +0 -183
  120. topicforge-0.5.5/examples/dds/01_who_is_on_the_bus/README.md +0 -58
  121. topicforge-0.5.5/examples/dds/03_a_node_crashed/README.md +0 -62
  122. topicforge-0.5.5/examples/dds/12_safety_monitor_dropout/README.md +0 -70
  123. topicforge-0.5.5/src/topicforge/adapters/base.py +0 -143
  124. topicforge-0.5.5/src/topicforge/adapters/common/cdr_decoder.py +0 -203
  125. topicforge-0.5.5/src/topicforge/adapters/common/xtypes.py +0 -120
  126. topicforge-0.5.5/src/topicforge/adapters/dds_dust/__init__.py +0 -12
  127. topicforge-0.5.5/src/topicforge/adapters/dds_opendds/__init__.py +0 -15
  128. topicforge-0.5.5/src/topicforge/constants.py +0 -28
  129. topicforge-0.5.5/src/topicforge/services/bag_service.py +0 -273
  130. topicforge-0.5.5/src/topicforge/telemetry/__init__.py +0 -27
  131. {topicforge-0.5.5 → topicforge-0.5.6}/LICENSE +0 -0
  132. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/cyclone_c/README.md +0 -0
  133. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
  134. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
  135. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/dust_py/README.md +0 -0
  136. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/rti_c/README.md +0 -0
  137. {topicforge-0.5.5 → topicforge-0.5.6}/scripts/integration/publishers/rti_cpp/README.md +0 -0
  138. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/__main__.py +0 -0
  139. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/__init__.py +0 -0
  140. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/common/topic_filter.py +0 -0
  141. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  142. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  143. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  144. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  145. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/config/__init__.py +0 -0
  146. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/models/__init__.py +0 -0
  147. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/server/__init__.py +0 -0
  148. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/services/__init__.py +0 -0
  149. {topicforge-0.5.5 → topicforge-0.5.6}/src/topicforge/tools/__init__.py +0 -0
  150. {topicforge-0.5.5 → topicforge-0.5.6}/tests/__init__.py +0 -0
  151. {topicforge-0.5.5 → topicforge-0.5.6}/tests/fixtures/csv_echo_imu.txt +0 -0
  152. {topicforge-0.5.5 → topicforge-0.5.6}/tests/fixtures/csv_echo_pose_multi.txt +0 -0
  153. {topicforge-0.5.5 → topicforge-0.5.6}/tests/integration/__init__.py +0 -0
  154. {topicforge-0.5.5 → topicforge-0.5.6}/tests/integration/test_real_bus.py +0 -0
  155. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_example_node_spec.py +0 -0
  156. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_health.py +0 -0
  157. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_honest_outputs.py +0 -0
  158. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_live_adapter_parse.py +0 -0
  159. {topicforge-0.5.5 → topicforge-0.5.6}/tests/test_live_adapter_subprocess.py +0 -0
  160. {topicforge-0.5.5 → topicforge-0.5.6}/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,201 @@ 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.5.6] - 2026-10-02
9
+
10
+ Fixes from a live run of 0.5.3 and 0.5.5 against OmniSim's simulated
11
+ Clearpath Husky (ROS 2 Humble, Fast DDS, Cyclone backend for the DDS tools),
12
+ reported with ground truth by the OmniSim team.
13
+
14
+ ### Added
15
+
16
+ - `sample_messages` takes `max_array_length` (1..65536, default 128, null for no
17
+ cut) and `arrays_summary_only`. A cut is listed under `_truncated_after_columns`
18
+ in the sample and in `note`.
19
+ - Size caps on returned samples: 1 MiB per message and 4 MiB per call
20
+ (`TOPICFORGE_MAX_SAMPLE_BYTES` sets the per-message cap). Over-cap messages are
21
+ dropped and `note` says so. `peek_bag_samples` is capped the same way, and its
22
+ arrays are cut at 4096 elements.
23
+ - `BagTopicStats` gains `first_timestamp_ns`, `last_timestamp_ns`,
24
+ `frequency_basis` (`topic_span` or `bag_duration`) and `latched`.
25
+ - `TopicInfo.qos_durability`; `qos_reliability` and `qos_durability` are filled
26
+ by `get_topic_info` from the publishers' QoS (`mixed` when they disagree).
27
+ - `HealthReport.dds_inactive_reason` says why `dds_backend` is `none`.
28
+ - A real Humble rosbag2 bag from the OmniSim team as a test fixture
29
+ (`tests/fixtures/bags/omnisim_humble/`).
30
+
31
+ ### Changed
32
+
33
+ - `list_topics` (live) uses one `ros2 topic list -v` call for publisher and
34
+ subscriber counts instead of one `ros2 topic info` per topic, falling back to
35
+ the per-topic calls if the output is not recognized. It leaves QoS null.
36
+ - `rosbags>=0.11.3` is required (0.10 reads a bare `.db3` as ROS 1 and returns
37
+ message definitions and QoS as plain strings).
38
+ - A latched topic gets no rate only when its messages span under 1 second (a
39
+ start-up burst such as `/tf_static`); a latched topic published over a longer
40
+ span keeps its rate. `latched` is unchanged.
41
+ - `analyze_bag` reads per-topic times of an `.mcap` bag only up to 200 MiB and
42
+ 5 s; past either it keeps `bag_duration` rates and says so in the new
43
+ `BagAnalysis.note`.
44
+ - The `policies_checked` entry for History reads "History (risky only, where
45
+ announced)".
46
+ - `max_array_length` also cuts strings and bytes to that many characters plus
47
+ `...`; those cells are listed under `_truncated_columns`.
48
+ - `QosProfile.history` is now optional, and a new `history_note` explains why it
49
+ is missing. DDS discovery does not carry History (the builtin endpoint data has
50
+ no such member), so TopicForge reports it only for its own endpoints and for
51
+ Cyclone DDS peers that set something other than the default. For Fast DDS, RTI
52
+ and unknown-vendor endpoints it is `null`; for a Cyclone peer, KEEP_LAST depth 1
53
+ is also `null` because the binding fills missing QoS with that default. Clients
54
+ that assumed `history` is always a string must handle `null`.
55
+ - A discovered endpoint that announced no History keeps its reliability,
56
+ durability and the other policies; before, the whole `qos` became `null`.
57
+ - `detect_qos_mismatches` judges the KEEP_ALL-vs-KEEP_LAST History risk only
58
+ where both sides announced History; elsewhere one hint states that discovery
59
+ does not carry it, instead of a warning on every pair.
60
+
61
+ ### Fixed
62
+
63
+ - `sample_messages` with `arrays_summary_only` shifted every CSV column after an
64
+ array: `<sequence type: float, length: 541>` contains a comma and was split in
65
+ two. It is one cell now.
66
+ - `sample_messages` returned no samples and no explanation when the echo timed
67
+ out (for example a large message with `max_array_length` null); `note` now says
68
+ so.
69
+ - `list_topics` reported 0 publishers and 0 subscribers for a topic missing from
70
+ `ros2 topic list -v`; it now asks `ros2 topic info` for that topic.
71
+ - `peek_bag_samples` converted a whole numpy array to a list before cutting it
72
+ at 4096 elements; it now converts only the part it keeps.
73
+ - Fast DDS endpoints no longer report a History taken from the binding's
74
+ defaults: `detect_qos_mismatches` treats it as not announced, like the other
75
+ vendors. This path has never run against a real Fast DDS bus.
76
+ - The `tests/fixtures/bags` bag is left out of the sdist.
77
+ - `peek_bag_samples` failed on every Humble `.db3` bag with "Bag contains no
78
+ type definitions". The reader now gets the type definitions of the distro the
79
+ bag records, or Humble, and `note` says which. `LaserScan.ranges` and other
80
+ numeric arrays now come back as lists, not a numpy repr string.
81
+ - `analyze_bag` rates were count / whole-bag duration, 0.1 to 0.7 percent off on
82
+ periodic topics and meaningless on latched ones (`/tf_static` showed 0.06 Hz,
83
+ `/rosout` 0.37 Hz). They are now `(n - 1) / (last - first)` per topic, read
84
+ from the bag when it is readable locally, and latched topics are flagged.
85
+ - `get_topic_info` returned `qos_reliability: null` on every topic although the
86
+ CLI prints Reliability and Durability per endpoint.
87
+ - `peek_dds_samples('/scan')` reported the topic as not discovered while
88
+ `rt/scan` and `scan` worked; it now resolves the name like the other DDS tools
89
+ and says which topic matched.
90
+ - The "DDS module is not active" error said to install the Cyclone binding even
91
+ when it was installed. It now states the actual cause: backend not selected,
92
+ binding missing, or adapter failed to start. README wording aligned.
93
+ - CSV `...` truncation cells from `ros2 topic echo` no longer count as data
94
+ columns.
95
+ - `ros2` output is decoded as UTF-8 on Windows instead of cp1252.
96
+ - `detect_qos_mismatches` no longer calls service, action, `rosout`,
97
+ `parameter_events` or `ros_discovery_info` topics typos of each other (for
98
+ example `get_parametersRequest` vs `set_parametersRequest`), and no longer
99
+ reports them as orphans. Only plain `rt/` topics and bare DDS names are compared.
100
+ - The "differs by N edits" hint is now only given between a writer-only name and
101
+ a reader-only name; a topic with both sides is never suggested as a typo.
9
102
 
10
103
  ## [0.5.5] - 2026-10-02
11
104
 
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.
105
+ Tool outputs were reworked after testing them with LLM agents on the author's
106
+ 16 test scenarios (live DDS buses with planted faults).
107
+
108
+ ### Breaking
109
+
110
+ - `detect_qos_mismatches` returns a `MismatchScan` envelope instead of a bare
111
+ list. Migrate by reading `["reports"]` where you used the list.
17
112
 
18
113
  ### Added
19
114
 
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.
115
+ - `list_endpoints`, the twelfth tool: every announced DDS writer and reader with
116
+ owning participant, structured QoS and a per-topic roll-up that flags orphans
117
+ (`no_reader`, `no_writer`). Cyclone and mock only.
118
+ - Continuous discovery tracking on Cyclone, so a node restarted three times
119
+ shows as 3 `lost` and 4 `discovered` events dated by DDS, not by poll time.
120
+ - `announced_ns`, `lost_ns`, `time_source` and related timestamp fields on
121
+ `participant_events` and `list_participants`.
122
+ - `health_check` reports `now_ns`, tracker status and an observed-domain note.
123
+ - `QosProfile` gains Liveliness, Ownership, Partition, LatencyBudget,
124
+ DestinationOrder and DataRepresentation.
125
+ - `peek_dds_samples` on builtin discovery topics returns structured endpoint
126
+ and participant fields instead of a raw repr string.
127
+ - Topic filters accept `rt/x` and `x` interchangeably; a filter that matches
128
+ nothing returns the known topics.
129
+ - `detect_qos_mismatches` also reports `matched` and `not_matched` pairs, hints
130
+ (near-miss topic names, path suffixes, type id differences), and the policies
131
+ it did and did not check.
55
132
 
56
133
  ### Changed
57
134
 
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),
135
+ - `detect_qos_mismatches` checks Partition first (with `*` and `?` wildcards),
136
+ then type names, then the RxO rules; Liveliness, LatencyBudget, Ownership,
71
137
  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.
138
+ Deadline.
139
+ - `topic_metrics` and `peek_dds_samples` on a user topic say they have no data
140
+ instead of returning zeros or a placeholder.
141
+ - Tool descriptions no longer carry internal history.
93
142
 
94
143
  ### Fixed
95
144
 
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.
145
+ - Infinite durations are `None` instead of 9223372036854775807.
146
+ - Races between the tracker and tool calls could mark a live participant lost
147
+ or keep endpoints of a departed one.
148
+ - Concurrent cyclonedds calls could corrupt the heap on Windows; every binding
149
+ call now takes one lock.
150
+ - A failing tracker no longer adds 3 s to every tool call.
151
+ - An unreadable QoS duration is treated as unknown, not infinite.
152
+ - Typo hints stay bounded on a bus with a thousand topics.
116
153
 
117
154
  ### Known limits
118
155
 
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
156
+ - A hung writer (alive, no data) cannot be observed without a data probe.
157
+ - A crash and a clean leave look the same; `lost_ns` is an upper bound.
158
+ - A participant that cycles faster than the history depth between two tracker
159
+ passes can be missed.
160
+ - The Fast DDS backend has never run on a bus; `list_endpoints` is not
126
161
  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.
162
+ - DDS Security is not supported, and user-topic payload decoding is disabled.
130
163
 
131
164
  ## [0.5.4] - 2026-10-02
132
165
 
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.
166
+ First run of the DDS code against a live bus (Windows 11, a Python / Cyclone DDS
167
+ participant and a Rust / Dust DDS participant). It exposed four defects that
168
+ left the DDS module non-functional on Cyclone; all are fixed and tested.
138
169
 
139
170
  ### Fixed
140
171
 
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.
172
+ - `list_participants` reports the participant name and hostname on Cyclone
173
+ (both were always null).
174
+ - Participant GUIDs were never read, so every participant collapsed onto one
175
+ `unknown` entry.
176
+ - Vendors were never identified. The vendor id is now read from the GUID prefix;
177
+ Dust DDS and RTI by default still report `unknown`.
178
+ - The OMG vendor-id table mapped Fast DDS and Cyclone to the wrong ids (now
179
+ `01.0F` and `01.10`). The Fast DDS side is verified statically only.
180
+ - `ParticipantInfo.vendor` also accepts `rti_micro`, `opensplice`, `opendds`,
181
+ `coredx`, `intercom` and `dust`.
182
+ - `detect_qos_mismatches` never reported anything on Cyclone because policy
183
+ class names were not normalized.
184
+ - A stopped participant never disappeared; departed participants and endpoints
185
+ are now dropped.
186
+ - The Cyclone adapter leaked a reader per tool call and waited 2 s each; it now
187
+ keeps one reader per builtin topic.
188
+ - `peek_dds_samples` on `DCPSPublication` / `DCPSSubscription` listed endpoints
189
+ of participants that had left, and now includes the endpoint `type_name`.
190
+ - Example role nodes published below their rate on Windows.
183
191
 
184
192
  ### Added
185
193
 
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.
194
+ - `examples/dds/`: fourteen runnable examples (concepts and use cases), all
195
+ passing on a live bus.
196
+ - `scripts/integration/`: a demo driver, twelve interop programs (Cyclone, Dust,
197
+ Fast DDS, RTI, OpenSplice), one-command setup scripts and CI workflows. Only
198
+ Cyclone Python and Dust Rust / Python / C have been run, on Windows.
230
199
 
231
200
  ### Removed
232
201
 
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.
202
+ - The docker / scenario integration rig, replaced by the demo driver.
236
203
 
237
204
  ## [0.5.3] - 2026-10-01
238
205
 
@@ -1235,7 +1202,8 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
1235
1202
  - The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
1236
1203
  - `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
1237
1204
 
1238
- [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...HEAD
1205
+ [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.6...HEAD
1206
+ [0.5.6]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...v0.5.6
1239
1207
  [0.5.5]: https://github.com/yaniswav/TopicForge/compare/v0.5.4...v0.5.5
1240
1208
  [0.5.4]: https://github.com/yaniswav/TopicForge/compare/v0.5.3...v0.5.4
1241
1209
  [0.5.3]: https://github.com/yaniswav/TopicForge/compare/v0.5.2...v0.5.3