topicforge 0.4.0__tar.gz → 0.5.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 (107) hide show
  1. {topicforge-0.4.0 → topicforge-0.5.1}/.gitignore +6 -0
  2. {topicforge-0.4.0 → topicforge-0.5.1}/CHANGELOG.md +329 -2
  3. {topicforge-0.4.0 → topicforge-0.5.1}/PKG-INFO +28 -23
  4. {topicforge-0.4.0 → topicforge-0.5.1}/README.md +22 -20
  5. {topicforge-0.4.0 → topicforge-0.5.1}/docs/DDS_QUICKSTART.md +31 -22
  6. topicforge-0.5.1/docs/MIGRATION_v0.3_to_v0.4.md +242 -0
  7. {topicforge-0.4.0 → topicforge-0.5.1}/docs/TESTING.md +12 -3
  8. topicforge-0.5.1/docs/TROUBLESHOOTING.md +245 -0
  9. topicforge-0.5.1/docs/TUTORIEL.md +204 -0
  10. {topicforge-0.4.0 → topicforge-0.5.1}/docs/product-plan.md +4 -4
  11. topicforge-0.5.1/examples/README.md +30 -0
  12. {topicforge-0.4.0 → topicforge-0.5.1}/pyproject.toml +36 -2
  13. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/__init__.py +1 -1
  14. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/__init__.py +30 -0
  15. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/cdr_decoder.py +22 -5
  16. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/dds_helpers.py +33 -5
  17. topicforge-0.5.1/src/topicforge/adapters/common/dds_introspection.py +184 -0
  18. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/lifecycle.py +33 -4
  19. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/metrics_buffer.py +70 -20
  20. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/qos_analyzer.py +21 -9
  21. topicforge-0.5.1/src/topicforge/adapters/common/qos_endpoints.py +95 -0
  22. topicforge-0.5.1/src/topicforge/adapters/common/qos_normalize.py +178 -0
  23. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/xtypes.py +8 -5
  24. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_cyclone/adapter.py +105 -210
  25. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_dust/adapter.py +2 -2
  26. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_fast/adapter.py +91 -201
  27. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_opendds/adapter.py +16 -10
  28. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_mock/adapter.py +3 -4
  29. topicforge-0.5.1/src/topicforge/constants.py +28 -0
  30. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/bag_service.py +13 -16
  31. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/health.py +1 -1
  32. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/inspector.py +8 -11
  33. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/tools/handlers.py +8 -5
  34. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/test_scenarios_schema.py +2 -1
  35. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_bag_service.py +46 -2
  36. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_cdr_decoder.py +34 -0
  37. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dds_helpers.py +63 -0
  38. topicforge-0.5.1/tests/test_dds_introspection.py +215 -0
  39. topicforge-0.5.1/tests/test_dds_qos_normalization.py +286 -0
  40. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_health.py +1 -1
  41. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_lifecycle_buffer.py +38 -1
  42. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_metrics_buffer.py +85 -0
  43. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_opendds_adapter.py +4 -2
  44. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_qos_analyzer.py +21 -0
  45. topicforge-0.5.1/tests/test_qos_endpoints.py +156 -0
  46. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_tools_integration.py +23 -0
  47. topicforge-0.4.0/src/topicforge/services/constants.py +0 -21
  48. {topicforge-0.4.0 → topicforge-0.5.1}/LICENSE +0 -0
  49. {topicforge-0.4.0 → topicforge-0.5.1}/docs/MIGRATION_v0.1_to_v0.2.md +0 -0
  50. {topicforge-0.4.0 → topicforge-0.5.1}/docs/MIGRATION_v0.2_to_v0.3.md +0 -0
  51. {topicforge-0.4.0 → topicforge-0.5.1}/docs/dds-interop-matrix.md +0 -0
  52. {topicforge-0.4.0 → topicforge-0.5.1}/docs/pro.md +0 -0
  53. {topicforge-0.4.0 → topicforge-0.5.1}/scripts/integration/README.md +0 -0
  54. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/__main__.py +0 -0
  55. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/__init__.py +0 -0
  56. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/base.py +0 -0
  57. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/composite.py +0 -0
  58. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  59. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
  60. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  61. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
  62. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  63. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
  64. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  65. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_mock/fixtures.py +0 -0
  66. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/config/__init__.py +0 -0
  67. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/config/settings.py +0 -0
  68. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/models/__init__.py +0 -0
  69. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/models/schemas.py +0 -0
  70. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/server/__init__.py +0 -0
  71. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/server/app.py +0 -0
  72. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/__init__.py +0 -0
  73. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/factory.py +0 -0
  74. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/telemetry/__init__.py +0 -0
  75. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/telemetry/client.py +0 -0
  76. {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/tools/__init__.py +0 -0
  77. {topicforge-0.4.0 → topicforge-0.5.1}/tests/__init__.py +0 -0
  78. {topicforge-0.4.0 → topicforge-0.5.1}/tests/conftest.py +0 -0
  79. {topicforge-0.4.0 → topicforge-0.5.1}/tests/fixtures/csv_echo_imu.txt +0 -0
  80. {topicforge-0.4.0 → topicforge-0.5.1}/tests/fixtures/csv_echo_pose_multi.txt +0 -0
  81. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/__init__.py +0 -0
  82. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/conftest.py +0 -0
  83. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/lifecycle_tracking.json +0 -0
  84. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/multi_vendor_basic.json +0 -0
  85. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/qos_mismatch_detection.json +0 -0
  86. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/topic_metrics_frequency.json +0 -0
  87. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/topic_metrics_sequence_gaps.json +0 -0
  88. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/xtypes_decode.json +0 -0
  89. {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/test_real_bus.py +0 -0
  90. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_analyze_bag_multi_format.py +0 -0
  91. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_composite_adapter.py +0 -0
  92. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_config.py +0 -0
  93. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_cyclone_adapter.py +0 -0
  94. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dds_cross_vendor.py +0 -0
  95. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dds_schemas.py +0 -0
  96. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dust_adapter.py +0 -0
  97. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_factory.py +0 -0
  98. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_fast_adapter.py +0 -0
  99. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_inspector.py +0 -0
  100. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_live_adapter_parse.py +0 -0
  101. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_live_adapter_subprocess.py +0 -0
  102. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_mock_adapter.py +0 -0
  103. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_peek_bag_samples.py +0 -0
  104. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_pro_hook.py +0 -0
  105. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_telemetry.py +0 -0
  106. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_topic_metrics.py +0 -0
  107. {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_xtypes.py +0 -0
@@ -217,6 +217,12 @@ CLAUDE*.md
217
217
  # remain reproducible. Tracked, not part of sdist.
218
218
  !/docs/projet-file/references/
219
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/**
220
226
  /docs/assets/screencast-raw/
221
227
 
222
228
 
@@ -5,7 +5,331 @@ 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
- ## [0.4.0]
8
+ ## [Unreleased]
9
+
10
+ ## [0.5.1] - 2026-08-22
11
+
12
+ ### Fixed (hotfix — MCP SDK 2.0 incompatibility)
13
+
14
+ - **Hard-pinned `mcp < 2` — TopicForge was uninstallable from 2026-07-28
15
+ to 2026-08-22.** The MCP Python SDK released `2.0.0` on 2026-07-28
16
+ alongside the `2026-07-28` protocol revision. That release removes the
17
+ `mcp.server.fastmcp` module entirely (`mcp/server/` now ships `auth`,
18
+ `lowlevel`, and `mcpserver`), so `server/app.py`'s
19
+ `from mcp.server.fastmcp import FastMCP` raises `ImportError` against
20
+ it. The dependency was declared as an unbounded `mcp>=1.0.0`, so every
21
+ fresh `pip install topicforge` resolved to `2.0.0` and produced a
22
+ server that could not start. Local development and CI both masked the
23
+ break — the dev environment had `mcp 1.27.1` already installed, and
24
+ the last CI run predates the SDK release. Pinning `<2` restores
25
+ installability on the 1.x line; migrating to the 2.x API
26
+ (`FastMCP` -> `MCPServer`, transport options moved from the
27
+ constructor to `.run()`, stateless protocol) is tracked separately and
28
+ is deliberately **not** bundled into this hotfix.
29
+
30
+ ### Note on scope
31
+
32
+ - This release also publishes the external-audit work (Lots 0-5,
33
+ 2026-07-08) that had accumulated under `[Unreleased]`. The Cyclone
34
+ `take_iter` -> `read_iter` change documented below is still **validated
35
+ by static analysis only** (`ruff` + `py_compile`); it has not been
36
+ exercised against a real multi-vendor DDS bus. It ships here because
37
+ leaving the package uninstallable was the larger harm — a broken
38
+ install affects every user, while this change can only affect users
39
+ running the Cyclone backend against a live bus. Real-bus validation
40
+ remains open.
41
+
42
+ ### Changed
43
+
44
+ - **DDS pure logic extracted for testability (Lot 0, external audit
45
+ 2026-07-08).** The QoS-profile normalizers (`_cyclone_qos_to_profile` /
46
+ `_fast_qos_to_profile`) and the discovery-sample field extractors
47
+ (`_extract_guid` / `_extract_vendor_id` / `_extract_hostname` /
48
+ `_extract_topic_name` / `_is_removal`) were moved out of
49
+ `adapters/dds_cyclone/adapter.py` and `adapters/dds_fast/adapter.py` —
50
+ which import their vendor binding at module top level and were therefore
51
+ never exercised by the test suite — into the binding-free
52
+ `adapters/common/qos_normalize.py` and `adapters/common/dds_introspection.py`.
53
+ The adapters import them back under their original private names, so every
54
+ call site is unchanged (verified by `ruff check` static analysis, since the
55
+ adapters are not importable without their SDKs). `fast_qos_to_profile`
56
+ takes the binding's int→str enum maps as parameters so it stays
57
+ import-free. This is the highest-value item from the audit: the QoS
58
+ normalization feeds `detect_qos_mismatches` (the flagship DDS diagnostic)
59
+ and was previously untestable and untested.
60
+
61
+ ### Added
62
+
63
+ - `tests/test_dds_qos_normalization.py` and `tests/test_dds_introspection.py`
64
+ drive the extracted logic with synthetic duck-typed objects (no
65
+ `cyclonedds` / `fastdds` needed), including regression guards for the
66
+ "renamed policy key silently yields no QoS profile → no mismatch ever
67
+ reported" failure mode. The extracted modules are now ~91–92% covered.
68
+ - `pytest-cov` and `rosbags` added to the `[dev]` extra, plus `[tool.coverage]`
69
+ config with a `fail_under = 85` floor (the two binding-only adapter shells
70
+ are `omit`ted as structurally unreachable without their SDKs). Adding
71
+ `rosbags` un-skips the real `.db3` bag-analysis I/O test.
72
+
73
+ ### Fixed
74
+
75
+ - `tests/test_bag_service.py` bag-generation helper updated for the current
76
+ `rosbags` API (`Writer(..., version=Writer.VERSION_LATEST)`; typestore keyed
77
+ by `std_msgs/msg/String`, not `std_msgs__msg__String`). The test was
78
+ previously auto-skipped and had gone silently stale — un-skipping it in CI
79
+ surfaced the drift.
80
+ - **`topic_metrics` frequency was wrong (Lot 2, audit C5).** It divided the
81
+ sample count by `(now − oldest_sample)` — folding in idle time since the
82
+ last peek — and counted N intervals instead of N−1. Now measured as
83
+ `(N−1) / (newest − oldest)` over the samples' own arrival span; samples
84
+ surfaced by a single opportunistic peek share one timestamp (span 0) and
85
+ correctly yield `frequency_hz_observed = null` instead of a fabricated rate.
86
+ - **`topic_metrics` sequence-gap count exploded on multi-writer topics,
87
+ publisher restarts, and counter wrap (Lot 2, audit C6).** Gaps are now
88
+ counted per writer (new best-effort `MetricsSample.writer_guid`), so
89
+ independent writers' counter offsets are not read as phantom gaps, and a
90
+ single jump wider than 10 000 is treated as a reset/wrap discontinuity
91
+ rather than that many losses.
92
+ - **QoS Deadline false negative (Lot 2, audit C3/P1-3).**
93
+ `detect_qos_mismatches` now flags a reader that requests a finite Deadline
94
+ against a writer that offers none — an absent deadline is the infinite
95
+ (loosest) period and cannot satisfy a finite request. The previous rule
96
+ required both sides non-null and silently missed this incompatibility.
97
+ - **`LifecycleBuffer` participant map was unbounded (Lot 3, audit P1-4 / M1 /
98
+ P1).** Only the event ring was capped; the participant dict grew one entry
99
+ per GUID ever seen (a churny bus mints a fresh RTPS GUID on each node
100
+ restart), and `list_participants` returned every tombstone forever. Now
101
+ capped at `MAX_PARTICIPANTS = 4096`, evicting `"left"` tombstones first then
102
+ the oldest-inserted entry — the docstring's "Bounded" claim is now true.
103
+ - **`MetricsBuffer` topic map was unbounded (Lot 3, audit P2-5).** Per-topic
104
+ rings were capped but the number of topic keys was not; now capped at
105
+ `MAX_TOPICS = 4096`, oldest-inserted topic evicted on overflow.
106
+ - **OpenDDS stub `is_available()` always returns False (Lot 4, audit S1).** A
107
+ stub that advertised availability (when a `pyopendds` module happened to be
108
+ importable) could be auto-selected by the factory, after which every tool
109
+ call raised. Now consistent with the Dust stub.
110
+ - **`iter_field_names` mis-decoded a string `__slots__` (Lot 4, audit C2).**
111
+ `__slots__ = "value"` was exploded into `['v','a','l','u','e']`; a bare
112
+ string slot is now treated as a single field name.
113
+ - **`decode_field_value` recursion is depth-capped at 32 (Lot 4, audit M6).**
114
+ A pathologically deep decoded object graph collapses to `repr()` instead of
115
+ risking `RecursionError`.
116
+ - **`_encode_raw_bytes` slices before hex-encoding (Lot 4, audit M5).** A large
117
+ raw payload no longer allocates its full 2×-size hex string only to truncate
118
+ it to the 4096-char preview.
119
+
120
+ ### Added (Lot 4 — test hardening)
121
+
122
+ - End-to-end test that a failing tool call surfaces as an MCP error
123
+ (`ToolError`) rather than being masked as a success — pins the thin-handler
124
+ contract (CLAUDE.md §8, audit test-gap #4).
125
+ - Test pinning that every canonical vendor tag is a valid
126
+ `ParticipantInfo`/`ParticipantEvent.vendor` Literal (guards against
127
+ vendor-map ↔ schema drift, audit P2-3). Scenario allowlist `_KNOWN_TOOLS`
128
+ now includes the 11th tool `peek_bag_samples`.
129
+
130
+ ### Removed (Lot 4)
131
+
132
+ - Dead `annotate_full` / `annotate_partial` imports and the `_ = (...)`
133
+ unused-suppressor from `services/bag_service.py`.
134
+
135
+ ### Documentation (Lot 1 — reconcile the strategic source of truth)
136
+
137
+ - **`docs/product-plan.md` realigned on the shipped 11-tool surface (audit
138
+ M6/P1-6).** §1 and §4 said "five typed tools today" / DDS "roadmapped"
139
+ while six DDS/observability tools had shipped across v0.2.0–v0.4.0. §11's
140
+ risk register carried a self-imposed governance gate — "any 9th tool needs
141
+ an explicit re-scope discussion documented in this register before code
142
+ lands" — that was crossed during v0.4.0 without the discussion being
143
+ recorded. Added a retroactive re-scope decision closing that gap: the three
144
+ ceiling-breaking tools are accepted, the new ceiling is 11 tools, a 12th
145
+ needs a documented re-scope.
146
+ - **User-topic `raw` decode honesty (audit C1).** README and the
147
+ `peek_dds_samples` tool description no longer imply the `raw` fallback
148
+ preserves the payload in `_raw_bytes_hex` — on the current user-topic raw
149
+ path that field is empty (a `raw` status means "present but not decoded").
150
+ Capturing the on-wire CDR bytes is stated as roadmapped rather than done.
151
+
152
+ ### Changed (Lot 5 — DDS adapter deduplication)
153
+
154
+ - **QoS-mismatch endpoint pairing deduplicated (audit D1/M7/P2).** The ~40
155
+ identical lines in each adapter's `detect_qos_mismatches` (group endpoints by
156
+ topic, pair reader × writer, build `MismatchReport`) moved to the binding-free
157
+ `common/qos_endpoints.detect_mismatches_across_endpoints`, unit-tested without
158
+ a binding. Each writer's QoS profile is now parsed once per topic instead of
159
+ once per reader (fixes the O(readers × writers) re-parse). Both adapters
160
+ delegate to it.
161
+ - **Shared `validate_domain_id` (`common/dds_helpers`).** The identical 0..232
162
+ bound check in all four DDS adapter constructors (Cyclone, Fast, OpenDDS,
163
+ Dust) is now defined once.
164
+ - **Cyclone discovery/sample reads switched from `take_iter` to `read_iter`
165
+ (audit A1/P1-5).** Destructive `take` drained the builtin discovery cache,
166
+ risking spurious lost / re-discovered participant flapping across polls;
167
+ `read` is non-destructive — the correct choice for read-only observability.
168
+ ⚠️ **Requires real-bus validation on `scripts/integration/` before release**:
169
+ the read-vs-take semantics cannot be exercised without `cyclonedds` installed
170
+ (the adapter is not importable in the unit environment; these edits are
171
+ validated only by `ruff` static analysis + `py_compile`).
172
+
173
+ Baseline: 399 → 485 passed, 24 → 23 skipped, ruff clean, coverage 89.12%.
174
+
175
+ ## [0.5.0] - 2026-05-21
176
+
177
+ ### Sprint v0.5.0 — Polish + validation (pre-marketing-publication)
178
+
179
+ > Branch `feat/v0.5.0-polish-and-validation`. Three sub-milestones
180
+ > (5.1 real validation, 5.2 audit closure + error polish, 5.3 docs +
181
+ > examples + repo polish). No new MCP tool, no schema change. Last
182
+ > sprint before marketing publication ; the next version bump is
183
+ > the maintainer's manual `release` commit + tag.
184
+
185
+ #### Changed (sub-milestone 5.1 — Real validation)
186
+
187
+ - **CI matrix expanded** (`.github/workflows/ci.yml`) from
188
+ `ubuntu-latest × {3.11, 3.12}` to `{ubuntu-latest, windows-latest} ×
189
+ {3.11, 3.12, 3.13}` (6 cells, `fail-fast: false`). Windows-latest
190
+ coverage closes the largest unverified surface — TopicForge's primary
191
+ dev environment is Windows per `CLAUDE.md §7` and was previously only
192
+ hand-validated. Python 3.13 added now that wheels are stable across
193
+ the dependency footprint.
194
+ - **`pyproject.toml` classifiers** widened with `Programming Language ::
195
+ Python :: 3.13`.
196
+
197
+ #### Changed (sub-milestone 5.2 — AdapterError polish)
198
+
199
+ - **`DDS_ONLY_ERROR_MSG`** (`adapters/common/dds_helpers.py`) rewritten
200
+ with the v0.4.0 `CompositeAdapter` remediation path explicit, and
201
+ with every affected ROS2 tool name listed inline. Substring
202
+ `"DDS observability only"`, `"TOPICFORGE_DDS_BACKEND"`,
203
+ `"TOPICFORGE_MODE"` preserved — existing `pytest.raises(match=...)`
204
+ contracts honored. New token assertions added :
205
+ `"CompositeAdapter"`, the 4 affected tool names.
206
+ - **CycloneDDS adapter errors** (`adapters/dds_cyclone/adapter.py`)
207
+ enriched with the underlying exception type, the active domain id,
208
+ the topic name (where relevant), and common-cause diagnostic hints
209
+ (`CYCLONEDDS_URI` misconfiguration, multicast firewall, domain
210
+ mismatch). Three sites : participant discovery, endpoint discovery,
211
+ sample peek.
212
+ - **Fast DDS participant-init error** (`adapters/dds_fast/adapter.py`)
213
+ enriched with an ABI-mismatch diagnostic — Fast DDS 2.6.x Python
214
+ binding wheels frequently desynchronize with system-installed Fast
215
+ DDS native libraries, and the v0.4.0 wording masked the cause behind
216
+ a bare `"returned None"`. New message points at the pyproject pin
217
+ (`fastdds>=2.6.1,<3`) and the `FastDDS_DEFAULT_PROFILES_FILE` env
218
+ var as the two likely culprits.
219
+ - **`BagService` IO errors** (`services/bag_service.py`) wrap the
220
+ rosbags-side exception class name into the AdapterError message so
221
+ the LLM caller can pivot on `PermissionError` / `IsADirectoryError`
222
+ / etc. without inspecting `__cause__`.
223
+ - **`_ROSBAGS_REQUIRED_MSG`** rewording — clarifies that `analyze_bag`
224
+ has a v0.3.0 text-parse fallback while `peek_bag_samples` does not.
225
+
226
+ #### Closed (sub-milestone 5.2 — audit triage)
227
+
228
+ - **`docs/projet-file/audit-followup-triage-v0.2.0.md` refreshed**
229
+ against the current tree (was last touched pre-v0.3.0). Strict
230
+ B-class : 3 items now CLOSED (B6 in v0.2.0, B9 in v0.3.0, B10 in
231
+ v0.5.0). 6 items remain DEFER (B1-B5 hosted-context security
232
+ hardening ; B7, B8 wire-contract decisions for v0.6+).
233
+ - **`TODO(roadmap, audit-2026-05-14)` at `services/inspector.py:76`
234
+ retired as WONT-FIX-by-design** — the `list_topics` Inspector gate
235
+ is intentionally empty (no MCP-level args to validate). The comment
236
+ block is now a permanent design note rather than a roadmap pointer.
237
+
238
+ #### Added (sub-milestone 5.2 — regression tests)
239
+
240
+ - **5 new tests** in `tests/test_dds_helpers.py` and
241
+ `tests/test_bag_service.py` (regex-token assertions, not exact
242
+ wording) — pin the polished message contract without locking the
243
+ exact prose. Baseline grows from 394 to 399 passed ; 24 skipped
244
+ unchanged ; ruff clean.
245
+
246
+ #### Changed (sub-milestone 5.3 — Documentation cascade)
247
+
248
+ - **`README.md`** — CI badge added ; tagline refreshed from "v0.3.0"
249
+ to "v0.4.0" framing emphasising observability + bag analysis ; the
250
+ 3-row DDS tool mini-table grew into a 6-row table listing every
251
+ DDS / observability tool with its sub-milestone of origin ;
252
+ `peek_dds_samples` scope rewritten around `_decode_status` (full /
253
+ partial / raw) ; telemetry contract field description switched from
254
+ "five MVP tools" to "eleven MCP tools" ; `TOPICFORGE_DDS_BACKEND`
255
+ Literal values listed in the config reference ; Roadmap section
256
+ pruned of items that shipped in v0.4.0 (Composite adapter, XTypes
257
+ Cyclone push).
258
+ - **`docs/DDS_QUICKSTART.md`** — header bumped to v0.4.0+ ; §4
259
+ "Single-adapter limitation (v0.3.0)" replaced by "Composite adapter
260
+ (v0.4.0 Phase 1+)" with the new routing table ; §5 documents the
261
+ v0.4.0 Phase 1.5 best-effort XTypes story (no more
262
+ `AdapterError` on user topics) ; §6 "What's next" pruned of shipped
263
+ items ; §7 Troubleshooting updated.
264
+ - **`docs/TESTING.md`** — "five MCP tools" → "eleven MCP tools" in the
265
+ three documented occurrences ("Pick your path" table row, the lead
266
+ paragraph, Path 1 header). New v0.4.0 tool-surface callout above
267
+ the path picker.
268
+
269
+ #### Added (sub-milestone 5.3 — new docs and examples)
270
+
271
+ - **`docs/MIGRATION_v0.3_to_v0.4.md`** (new, sibling to the existing
272
+ `MIGRATION_v0.2_to_v0.3.md`). 8 sections : new tools, env vars and
273
+ extras, soft-breaking schema widening (`ParticipantInfo` +4 fields,
274
+ `BagAnalysis` +4 fields, `HealthReport.ros_backend`, `dds_backend`
275
+ widening, `AdapterName` widening, new `TopicMetrics` /
276
+ `ParticipantEvent` schemas), `CompositeAdapter`, `peek_dds_samples`
277
+ user-topic story, protocol expansions, plus a quick checklist.
278
+ - **`docs/TROUBLESHOOTING.md`** (new). One section per polished
279
+ AdapterError message, plus the cross-cutting "ROS2 CLI not found on
280
+ PATH" / `auto` fallback to mock case. Each section quotes the
281
+ message and lists the diagnostics in order.
282
+ - **`examples/`** (new). 4 mock-mode-runnable walkthroughs covering
283
+ the headline value props :
284
+ - `01-discover-ros2-stack.md` — `health_check` + `list_topics` +
285
+ `get_topic_info` + `sample_messages`
286
+ - `02-debug-qos-mismatch.md` — `list_participants` +
287
+ `detect_qos_mismatches` + `peek_dds_samples` (canonical
288
+ Reliability mismatch story)
289
+ - `03-analyze-recording.md` — `analyze_bag` + `peek_bag_samples`
290
+ post-mortem inspection
291
+ - `04-monitor-topic-frequency.md` — `topic_metrics` +
292
+ `participant_events` with the opportunistic-fill caveat
293
+ Each example pairs an MCP-client prompt with the expected tool
294
+ calls and a short LLM-facing synthesis.
295
+
296
+ #### Added (sub-milestone 5.3 — repo polish)
297
+
298
+ - **`CONTRIBUTING.md`** (new). What contributions land easily vs hard,
299
+ development setup, the `make check` contract, mock-first
300
+ development convention, layer separation, pure-parser convention,
301
+ commit conventions.
302
+ - **`SECURITY.md`** (new). Local-trust threat model, the
303
+ read-only-by-architecture stance, vulnerability disclosure email,
304
+ response SLAs, supported version policy.
305
+ - **`.github/ISSUE_TEMPLATE/bug_report.yml`** (new) — structured form
306
+ with version, mode, OS, Python, repro, env vars, troubleshooting
307
+ check.
308
+ - **`.github/ISSUE_TEMPLATE/feature_request.yml`** (new) —
309
+ problem-first framing, tier disambiguation (OSS / Pro), explicit
310
+ read-only-by-architecture acknowledgment.
311
+ - **`.github/ISSUE_TEMPLATE/config.yml`** (new) — disables blank
312
+ issues, links to `SECURITY.md`, `docs/TROUBLESHOOTING.md`,
313
+ `docs/DDS_QUICKSTART.md`.
314
+ - **`.github/PULL_REQUEST_TEMPLATE.md`** (new) — summary, test plan,
315
+ backward-compatibility checklist (covers the 11-tool cap, schema
316
+ additive-only invariant, telemetry 6-field contract, env var docs,
317
+ public API removal flag).
318
+
319
+ #### Notes
320
+
321
+ - **Backward compat preserved.** Zero schema changes, zero new tools,
322
+ zero env-var renames. The `DDS_ONLY_ERROR_MSG` substring tokens
323
+ pinned by existing tests (`"DDS observability only"`,
324
+ `"TOPICFORGE_DDS_BACKEND"`, `"TOPICFORGE_MODE"`) are preserved
325
+ intact ; v0.4.0 producers and clients keep working byte-for-byte.
326
+ - **CHANGELOG entry deferred to release time.** This branch leaves
327
+ `pyproject.toml` at `0.4.0`, `__version__` at `"0.4.0"`, and the
328
+ `## [Unreleased]` heading populated with the polish notes above.
329
+ The version bump and `v0.5.0` tag are the maintainer's manual
330
+ steps after final review.
331
+
332
+ ## [0.4.0] - 2026-05-15
9
333
 
10
334
  ### Sprint v0.4.0 — Phase 3 (bag analysis multi-format)
11
335
 
@@ -554,7 +878,10 @@ Initial MVP release of TopicForge — ROS Topic Inspector & Bag Analyzer MCP ser
554
878
  - The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
555
879
  - `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
556
880
 
557
- [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.3.0...HEAD
881
+ [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.1...HEAD
882
+ [0.5.1]: https://github.com/yaniswav/TopicForge/compare/v0.5.0...v0.5.1
883
+ [0.5.0]: https://github.com/yaniswav/TopicForge/compare/v0.4.0...v0.5.0
884
+ [0.4.0]: https://github.com/yaniswav/TopicForge/compare/v0.3.0...v0.4.0
558
885
  [0.3.0]: https://github.com/yaniswav/TopicForge/compare/v0.2.0...v0.3.0
559
886
  [0.2.0]: https://github.com/yaniswav/TopicForge/compare/v0.1.2...v0.2.0
560
887
  [0.1.2]: https://github.com/yaniswav/TopicForge/compare/v0.1.1...v0.1.2
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: topicforge
3
- Version: 0.4.0
3
+ Version: 0.5.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
@@ -37,8 +37,9 @@ Classifier: Operating System :: OS Independent
37
37
  Classifier: Programming Language :: Python :: 3
38
38
  Classifier: Programming Language :: Python :: 3.11
39
39
  Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
40
41
  Requires-Python: >=3.11
41
- Requires-Dist: mcp>=1.0.0
42
+ Requires-Dist: mcp<2,>=1.0.0
42
43
  Requires-Dist: pydantic>=2.6
43
44
  Provides-Extra: all
44
45
  Requires-Dist: cyclonedds>=0.10; extra == 'all'
@@ -62,18 +63,21 @@ Requires-Dist: fastdds<3,>=2.6.1; extra == 'dds-fast'
62
63
  Provides-Extra: dds-opendds
63
64
  Requires-Dist: pyopendds>=0.1; extra == 'dds-opendds'
64
65
  Provides-Extra: dev
66
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
65
67
  Requires-Dist: pytest>=8.0; extra == 'dev'
68
+ Requires-Dist: rosbags>=0.9; extra == 'dev'
66
69
  Requires-Dist: ruff>=0.4; extra == 'dev'
67
70
  Description-Content-Type: text/markdown
68
71
 
69
72
  # TopicForge
70
73
 
71
74
  [![PyPI version](https://img.shields.io/pypi/v/topicforge.svg)](https://pypi.org/project/topicforge/)
75
+ [![CI](https://github.com/yaniswav/TopicForge/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/yaniswav/TopicForge/actions/workflows/ci.yml)
72
76
  [![Python versions](https://img.shields.io/pypi/pyversions/topicforge.svg)](https://pypi.org/project/topicforge/)
73
77
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/yaniswav/TopicForge/blob/main/LICENSE)
74
78
  [![Read-only by architecture](https://img.shields.io/badge/safety-read--only_by_architecture-2563eb)](https://github.com/yaniswav/TopicForge#security-model)
75
79
 
76
- > **The safety-first read-only MCP for ROS2 robotics — now with multi-vendor OMG DDS-RTPS observability (v0.3.0).** TopicForge lets AI agents inspect your ROS2 graph, ROS bag files, and (since v0.2.0) the raw DDS layer beneath ROS, without ever publishing back to the bus. v0.3.0 ships two OSS Python adapters — Eclipse CycloneDDS and eProsima Fast DDS — each joining the bus as a read-only DDS-RTPS participant that observes **every conformant vendor on the wire** (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) via the OMG protocol guarantee. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the OMG May 2025 interop reference.
80
+ > **The safety-first read-only MCP for ROS2 robotics — observability + multi-vendor OMG DDS-RTPS (v0.4.0).** TopicForge lets AI agents inspect your ROS2 graph, recorded bag files, and the raw DDS layer beneath ROS — without ever publishing back to the bus. **Eleven typed read-only tools** (5 ROS2 graph + 3 DDS + 3 observability/bag) share a single Pydantic envelope so an LLM caller reads one schema across the whole stack. v0.4.0 adds participant lifecycle tracking (`participant_events`), temporal metrics (`topic_metrics`), and post-mortem bag sample peek (`peek_bag_samples`) on top of v0.3.0's two OSS Python DDS participants — Eclipse CycloneDDS and eProsima Fast DDS — each observing **every conformant vendor on the wire** (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) via the OMG protocol guarantee. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the OMG May 2025 interop reference.
77
81
 
78
82
  TopicForge is a production-minded MCP (Model Context Protocol) server that lets AI agents — such as Claude — inspect ROS2 topics, analyze ROS bag files, and (since v0.2.0) observe the raw DDS layer through a clean, structured tool interface. It is read-only by **architecture**, not by configuration: there is no write path to misconfigure, no permission system to audit, no liability conversation to have. The MCP client can see the robot stack; it cannot touch it.
79
83
 
@@ -239,21 +243,24 @@ CoreDX, InterCOM) ship under the optional `topicforge-pro` package with
239
243
  BYO vendor license. See [`docs/pro.md`](docs/pro.md) for the early-access
240
244
  slot and pricing terms ; nothing is collected today.
241
245
 
242
- Three new MCP tools (in addition to the five ROS2 tools above) :
246
+ Six DDS / observability tools (in addition to the five ROS2 tools above) :
243
247
 
244
- | Tool | Purpose |
245
- | ----------------------- | -------------------------------------------------------------------------------- |
246
- | `list_participants` | DDS participants discovered on a domain, with vendor and hostname |
247
- | `detect_qos_mismatches` | Reader/writer QoS incompatibilities preventing communication on a topic |
248
- | `peek_dds_samples` | Recent samples on a raw DDS topic (distinct from `sample_messages` on ROS2 graph)|
248
+ | Tool | Since | Purpose |
249
+ | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
250
+ | `list_participants` | v0.2.0 | DDS participants discovered on a domain, with vendor, hostname, and (v0.4.0) lifecycle fields |
251
+ | `detect_qos_mismatches` | v0.2.0 | Reader/writer QoS incompatibilities preventing communication on a topic |
252
+ | `peek_dds_samples` | v0.2.0 | Recent samples on a raw DDS topic — v0.4.0 adds best-effort XTypes user-topic decode (distinct from `sample_messages` on ROS2 graph) |
253
+ | `participant_events` | v0.4.0 | Lifecycle stream — `discovered` / `lost` participant events over a configurable window |
254
+ | `topic_metrics` | v0.4.0 | Temporal metrics — observed frequency, sequence gaps, latency p50/p95/p99 over a sliding window |
255
+ | `peek_bag_samples` | v0.4.0 | Post-mortem inspection — decoded samples from a recorded `.mcap` / `.db3` / `.bag` file |
249
256
 
250
- **Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the 3 DDS tools (+ `participant_events` from Phase 1) hit the DDS backend. ROS2-only or DDS-only setups still work — the missing half is skipped and the present half serves what it can. The mock backend continues to expose all 9 tools against deterministic fixtures for local development.
257
+ **Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the DDS / observability tools hit the DDS backend. ROS2-only or DDS-only setups still work — the missing half is skipped and the present half serves what it can. The mock backend continues to expose all 11 tools against deterministic fixtures for local development.
251
258
 
252
- **v0.3.0 limitation — `peek_dds_samples` scope.** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`) ; arbitrary user topics raise an `AdapterError` pointing at the v0.3.x XTypes/IDL roadmap. The other two DDS tools (`list_participants`, `detect_qos_mismatches`) work end-to-end on any user-topic deployment.
259
+ **`peek_dds_samples` payload shape (v0.4.0 Phase 1.5).** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Arbitrary user topics return best-effort decoded samples with a `_decode_status` annotation : `"full"` (every IDL field decoded — currently a v0.4.0+ Cyclone XTypes path), `"partial"` (some fields decoded, others opaque), or `"raw"` (binding could not resolve the dynamic XTypes). The diagnostic key `_decode_note` carries a short explanation when the status is non-`full`. The wire shape is identical across Cyclone and Fast backends. **Caveat (v0.5.x):** on the current user-topic *raw* path `_raw_bytes_hex` is **empty** — a `"raw"` status means "topic present on the bus but not decoded", not "here are the serialized bytes to re-decode". Capturing the on-wire CDR bytes into the fallback is roadmapped ; until then use the Cyclone full/partial XTypes path for actual user-topic payloads. The 4 builtin DCPS topics are unaffected (always structured).
253
260
 
254
261
  **`RTI Connext`** is v0.4.0+ Pro tier (BYO license — see `docs/pro.md`).
255
262
 
256
- **Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration from v0.2.0 in [`docs/MIGRATION_v0.2_to_v0.3.md`](docs/MIGRATION_v0.2_to_v0.3.md).
263
+ **Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration history : [v0.2 → v0.3](docs/MIGRATION_v0.2_to_v0.3.md), [v0.3 → v0.4](docs/MIGRATION_v0.3_to_v0.4.md).
257
264
 
258
265
  ### Configure with Claude Desktop
259
266
 
@@ -308,7 +315,7 @@ make check # both, plus tests (CI bundle)
308
315
  | `TOPICFORGE_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
309
316
  | `TOPICFORGE_ROS2_BIN` | `ros2` | Name (or path) of the ROS2 CLI binary |
310
317
  | `TOPICFORGE_TELEMETRY` | `off` | Opt-in anonymous usage telemetry. See [Telemetry](#telemetry). |
311
- | `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, or `auto`. `auto` resolves to Fast > Cyclone > Mock. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
318
+ | `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, `opensplice`, `coredx`, `intercom`, `opendds`, `dust`, or `auto`. The v0.4.0 Phase 1.5 auto-detect chain resolves to: `rti > opensplice > coredx > intercom` (Pro tier, if installed) `> opendds > fast > cyclone > dust > mock`. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
312
319
  | `TOPICFORGE_DDS_DOMAIN_ID` | `0` | DDS domain id observed (0..232) when a DDS backend is active. |
313
320
 
314
321
  See [`.env.example`](.env.example).
@@ -336,7 +343,7 @@ When telemetry is on, each MCP tool call emits a single event with **only** thes
336
343
 
337
344
  | Field | Example | Notes |
338
345
  | ----------------- | ---------------- | ---------------------------------------------------------------------- |
339
- | `tool_name` | `"list_topics"` | One of the five MVP tools — never argument values. |
346
+ | `tool_name` | `"list_topics"` | One of the eleven MCP tools — never argument values. |
340
347
  | `latency_ms` | `12.34` | Wall-clock duration of the handler, rounded to 2 decimals. |
341
348
  | `mode` | `"mock"` | Effective runtime mode: `mock` or `live`. |
342
349
  | `version` | `"0.1.2"` | TopicForge server version. |
@@ -382,16 +389,14 @@ See [`docs/product-plan.md`](docs/product-plan.md) for the full product trajecto
382
389
 
383
390
  Near-term additions on the bench:
384
391
 
385
- - `rclpy`-backed live adapter for faster & richer sampling
386
- - XTypes/IDL discovery to extend `peek_dds_samples` to arbitrary user topics (today: 4 builtin DCPS topics only) — v0.3.x patch
387
- - Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.3.x patch
388
- - Composite adapter delegating per-tool category, so ROS2 + DDS surfaces work simultaneously — v0.3.x patch
389
- - `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — v0.4.0+
390
- - URDF inspector / validator MCP tools
391
- - Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health)
392
+ - `rclpy`-backed live adapter for faster & richer sampling (per-message rmw receive timestamps, windowed sampling) — gated on external user demand
393
+ - Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.5.x patch
394
+ - Real `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — the v0.4.0 Phase 1.5 framework is in place ; production binding pending Pro tier launch
395
+ - URDF inspector / validator MCP tools (Pro tier)
396
+ - Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health) — Pro tier
392
397
  - Dataset export helpers (rosbag → COCO / HF Datasets)
393
398
  - Synthetic data pipeline controller (Blender, Gazebo, Isaac Sim)
394
- - Hosted MCP endpoint with auth
399
+ - Hosted MCP endpoint with auth (Phase 3 — depends on Pro tier traction)
395
400
 
396
401
  ## Project layout
397
402
 
@@ -1,11 +1,12 @@
1
1
  # TopicForge
2
2
 
3
3
  [![PyPI version](https://img.shields.io/pypi/v/topicforge.svg)](https://pypi.org/project/topicforge/)
4
+ [![CI](https://github.com/yaniswav/TopicForge/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/yaniswav/TopicForge/actions/workflows/ci.yml)
4
5
  [![Python versions](https://img.shields.io/pypi/pyversions/topicforge.svg)](https://pypi.org/project/topicforge/)
5
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/yaniswav/TopicForge/blob/main/LICENSE)
6
7
  [![Read-only by architecture](https://img.shields.io/badge/safety-read--only_by_architecture-2563eb)](https://github.com/yaniswav/TopicForge#security-model)
7
8
 
8
- > **The safety-first read-only MCP for ROS2 robotics — now with multi-vendor OMG DDS-RTPS observability (v0.3.0).** TopicForge lets AI agents inspect your ROS2 graph, ROS bag files, and (since v0.2.0) the raw DDS layer beneath ROS, without ever publishing back to the bus. v0.3.0 ships two OSS Python adapters — Eclipse CycloneDDS and eProsima Fast DDS — each joining the bus as a read-only DDS-RTPS participant that observes **every conformant vendor on the wire** (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) via the OMG protocol guarantee. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the OMG May 2025 interop reference.
9
+ > **The safety-first read-only MCP for ROS2 robotics — observability + multi-vendor OMG DDS-RTPS (v0.4.0).** TopicForge lets AI agents inspect your ROS2 graph, recorded bag files, and the raw DDS layer beneath ROS — without ever publishing back to the bus. **Eleven typed read-only tools** (5 ROS2 graph + 3 DDS + 3 observability/bag) share a single Pydantic envelope so an LLM caller reads one schema across the whole stack. v0.4.0 adds participant lifecycle tracking (`participant_events`), temporal metrics (`topic_metrics`), and post-mortem bag sample peek (`peek_bag_samples`) on top of v0.3.0's two OSS Python DDS participants — Eclipse CycloneDDS and eProsima Fast DDS — each observing **every conformant vendor on the wire** (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) via the OMG protocol guarantee. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the OMG May 2025 interop reference.
9
10
 
10
11
  TopicForge is a production-minded MCP (Model Context Protocol) server that lets AI agents — such as Claude — inspect ROS2 topics, analyze ROS bag files, and (since v0.2.0) observe the raw DDS layer through a clean, structured tool interface. It is read-only by **architecture**, not by configuration: there is no write path to misconfigure, no permission system to audit, no liability conversation to have. The MCP client can see the robot stack; it cannot touch it.
11
12
 
@@ -171,21 +172,24 @@ CoreDX, InterCOM) ship under the optional `topicforge-pro` package with
171
172
  BYO vendor license. See [`docs/pro.md`](docs/pro.md) for the early-access
172
173
  slot and pricing terms ; nothing is collected today.
173
174
 
174
- Three new MCP tools (in addition to the five ROS2 tools above) :
175
+ Six DDS / observability tools (in addition to the five ROS2 tools above) :
175
176
 
176
- | Tool | Purpose |
177
- | ----------------------- | -------------------------------------------------------------------------------- |
178
- | `list_participants` | DDS participants discovered on a domain, with vendor and hostname |
179
- | `detect_qos_mismatches` | Reader/writer QoS incompatibilities preventing communication on a topic |
180
- | `peek_dds_samples` | Recent samples on a raw DDS topic (distinct from `sample_messages` on ROS2 graph)|
177
+ | Tool | Since | Purpose |
178
+ | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
179
+ | `list_participants` | v0.2.0 | DDS participants discovered on a domain, with vendor, hostname, and (v0.4.0) lifecycle fields |
180
+ | `detect_qos_mismatches` | v0.2.0 | Reader/writer QoS incompatibilities preventing communication on a topic |
181
+ | `peek_dds_samples` | v0.2.0 | Recent samples on a raw DDS topic — v0.4.0 adds best-effort XTypes user-topic decode (distinct from `sample_messages` on ROS2 graph) |
182
+ | `participant_events` | v0.4.0 | Lifecycle stream — `discovered` / `lost` participant events over a configurable window |
183
+ | `topic_metrics` | v0.4.0 | Temporal metrics — observed frequency, sequence gaps, latency p50/p95/p99 over a sliding window |
184
+ | `peek_bag_samples` | v0.4.0 | Post-mortem inspection — decoded samples from a recorded `.mcap` / `.db3` / `.bag` file |
181
185
 
182
- **Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the 3 DDS tools (+ `participant_events` from Phase 1) hit the DDS backend. ROS2-only or DDS-only setups still work — the missing half is skipped and the present half serves what it can. The mock backend continues to expose all 9 tools against deterministic fixtures for local development.
186
+ **Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the DDS / observability tools hit the DDS backend. ROS2-only or DDS-only setups still work — the missing half is skipped and the present half serves what it can. The mock backend continues to expose all 11 tools against deterministic fixtures for local development.
183
187
 
184
- **v0.3.0 limitation — `peek_dds_samples` scope.** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`) ; arbitrary user topics raise an `AdapterError` pointing at the v0.3.x XTypes/IDL roadmap. The other two DDS tools (`list_participants`, `detect_qos_mismatches`) work end-to-end on any user-topic deployment.
188
+ **`peek_dds_samples` payload shape (v0.4.0 Phase 1.5).** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Arbitrary user topics return best-effort decoded samples with a `_decode_status` annotation : `"full"` (every IDL field decoded — currently a v0.4.0+ Cyclone XTypes path), `"partial"` (some fields decoded, others opaque), or `"raw"` (binding could not resolve the dynamic XTypes). The diagnostic key `_decode_note` carries a short explanation when the status is non-`full`. The wire shape is identical across Cyclone and Fast backends. **Caveat (v0.5.x):** on the current user-topic *raw* path `_raw_bytes_hex` is **empty** — a `"raw"` status means "topic present on the bus but not decoded", not "here are the serialized bytes to re-decode". Capturing the on-wire CDR bytes into the fallback is roadmapped ; until then use the Cyclone full/partial XTypes path for actual user-topic payloads. The 4 builtin DCPS topics are unaffected (always structured).
185
189
 
186
190
  **`RTI Connext`** is v0.4.0+ Pro tier (BYO license — see `docs/pro.md`).
187
191
 
188
- **Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration from v0.2.0 in [`docs/MIGRATION_v0.2_to_v0.3.md`](docs/MIGRATION_v0.2_to_v0.3.md).
192
+ **Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration history : [v0.2 → v0.3](docs/MIGRATION_v0.2_to_v0.3.md), [v0.3 → v0.4](docs/MIGRATION_v0.3_to_v0.4.md).
189
193
 
190
194
  ### Configure with Claude Desktop
191
195
 
@@ -240,7 +244,7 @@ make check # both, plus tests (CI bundle)
240
244
  | `TOPICFORGE_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
241
245
  | `TOPICFORGE_ROS2_BIN` | `ros2` | Name (or path) of the ROS2 CLI binary |
242
246
  | `TOPICFORGE_TELEMETRY` | `off` | Opt-in anonymous usage telemetry. See [Telemetry](#telemetry). |
243
- | `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, or `auto`. `auto` resolves to Fast > Cyclone > Mock. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
247
+ | `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, `opensplice`, `coredx`, `intercom`, `opendds`, `dust`, or `auto`. The v0.4.0 Phase 1.5 auto-detect chain resolves to: `rti > opensplice > coredx > intercom` (Pro tier, if installed) `> opendds > fast > cyclone > dust > mock`. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
244
248
  | `TOPICFORGE_DDS_DOMAIN_ID` | `0` | DDS domain id observed (0..232) when a DDS backend is active. |
245
249
 
246
250
  See [`.env.example`](.env.example).
@@ -268,7 +272,7 @@ When telemetry is on, each MCP tool call emits a single event with **only** thes
268
272
 
269
273
  | Field | Example | Notes |
270
274
  | ----------------- | ---------------- | ---------------------------------------------------------------------- |
271
- | `tool_name` | `"list_topics"` | One of the five MVP tools — never argument values. |
275
+ | `tool_name` | `"list_topics"` | One of the eleven MCP tools — never argument values. |
272
276
  | `latency_ms` | `12.34` | Wall-clock duration of the handler, rounded to 2 decimals. |
273
277
  | `mode` | `"mock"` | Effective runtime mode: `mock` or `live`. |
274
278
  | `version` | `"0.1.2"` | TopicForge server version. |
@@ -314,16 +318,14 @@ See [`docs/product-plan.md`](docs/product-plan.md) for the full product trajecto
314
318
 
315
319
  Near-term additions on the bench:
316
320
 
317
- - `rclpy`-backed live adapter for faster & richer sampling
318
- - XTypes/IDL discovery to extend `peek_dds_samples` to arbitrary user topics (today: 4 builtin DCPS topics only) — v0.3.x patch
319
- - Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.3.x patch
320
- - Composite adapter delegating per-tool category, so ROS2 + DDS surfaces work simultaneously — v0.3.x patch
321
- - `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — v0.4.0+
322
- - URDF inspector / validator MCP tools
323
- - Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health)
321
+ - `rclpy`-backed live adapter for faster & richer sampling (per-message rmw receive timestamps, windowed sampling) — gated on external user demand
322
+ - Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.5.x patch
323
+ - Real `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — the v0.4.0 Phase 1.5 framework is in place ; production binding pending Pro tier launch
324
+ - URDF inspector / validator MCP tools (Pro tier)
325
+ - Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health) — Pro tier
324
326
  - Dataset export helpers (rosbag → COCO / HF Datasets)
325
327
  - Synthetic data pipeline controller (Blender, Gazebo, Isaac Sim)
326
- - Hosted MCP endpoint with auth
328
+ - Hosted MCP endpoint with auth (Phase 3 — depends on Pro tier traction)
327
329
 
328
330
  ## Project layout
329
331