topicforge 0.5.4__tar.gz → 0.5.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {topicforge-0.5.4 → topicforge-0.5.5}/CHANGELOG.md +123 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/PKG-INFO +12 -8
- {topicforge-0.5.4 → topicforge-0.5.5}/README.md +11 -7
- topicforge-0.5.5/docs/DDS_QUICKSTART.md +126 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/docs/TESTING.md +2 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/docs/TROUBLESHOOTING.md +1 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/docs/TUTORIEL.md +1 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/docs/dds-interop-matrix.md +2 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/docs/pro.md +1 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/docs/product-plan.md +3 -3
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/README.md +1 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/02_why_cant_they_talk/README.md +6 -3
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/03_a_node_crashed/README.md +9 -8
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/04_late_joiner_misses_data/README.md +2 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/08_crash_seen_from_inside/README.md +6 -5
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/10_lidar_silent_after_driver_swap/README.md +8 -4
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/11_who_talks_to_whom/README.md +2 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/12_safety_monitor_dropout/README.md +6 -3
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/14_restart_loop/README.md +5 -4
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/README.md +9 -5
- {topicforge-0.5.4 → topicforge-0.5.5}/pyproject.toml +1 -1
- topicforge-0.5.5/scripts/agent_eval/README.md +37 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/__init__.py +1 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/base.py +11 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/__init__.py +65 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/dds_helpers.py +50 -3
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/dds_introspection.py +4 -4
- topicforge-0.5.5/src/topicforge/adapters/common/discovery_tracker.py +497 -0
- topicforge-0.5.5/src/topicforge/adapters/common/endpoints.py +394 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/lifecycle.py +48 -7
- topicforge-0.5.5/src/topicforge/adapters/common/qos_analyzer.py +304 -0
- topicforge-0.5.5/src/topicforge/adapters/common/qos_endpoints.py +73 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/qos_normalize.py +120 -7
- topicforge-0.5.5/src/topicforge/adapters/common/qos_scan.py +385 -0
- topicforge-0.5.5/src/topicforge/adapters/common/topic_filter.py +106 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/composite.py +24 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_cyclone/adapter.py +201 -179
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_dust/adapter.py +12 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_fast/adapter.py +22 -20
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_opendds/adapter.py +12 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/ros2_live/adapter.py +12 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/ros2_mock/adapter.py +13 -3
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/ros2_mock/fixtures.py +124 -19
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/models/__init__.py +16 -0
- topicforge-0.5.5/src/topicforge/models/schemas.py +1281 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/services/health.py +33 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/services/inspector.py +31 -2
- topicforge-0.5.5/src/topicforge/tools/handlers.py +601 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_composite_adapter.py +5 -4
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_cyclone_adapter.py +74 -5
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_dds_cross_vendor.py +5 -5
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_dds_helpers.py +1 -1
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_dds_qos_normalization.py +44 -0
- topicforge-0.5.5/tests/test_discovery_tracker.py +462 -0
- topicforge-0.5.5/tests/test_endpoints.py +444 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_example_node_spec.py +72 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_factory.py +4 -3
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_health.py +22 -0
- topicforge-0.5.5/tests/test_honest_outputs.py +250 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_mock_adapter.py +17 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_qos_analyzer.py +205 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_qos_endpoints.py +18 -13
- topicforge-0.5.5/tests/test_qos_scan.py +388 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_tools_integration.py +9 -0
- topicforge-0.5.4/docs/DDS_QUICKSTART.md +0 -121
- topicforge-0.5.4/src/topicforge/adapters/common/qos_analyzer.py +0 -98
- topicforge-0.5.4/src/topicforge/adapters/common/qos_endpoints.py +0 -95
- topicforge-0.5.4/src/topicforge/models/schemas.py +0 -706
- topicforge-0.5.4/src/topicforge/tools/handlers.py +0 -453
- {topicforge-0.5.4 → topicforge-0.5.5}/.gitignore +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/LICENSE +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/00_hello_pub_sub/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/01_who_is_on_the_bus/README.md +2 -2
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/05_reliability_in_code/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/07_deadline_in_code/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/examples/dds/13_deadline_not_offered/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/cyclone_c/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/dust_py/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/fast_py/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/rti_c/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/scripts/integration/publishers/rti_cpp/README.md +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/__main__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/cdr_decoder.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/common/xtypes.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/config/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/config/settings.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/constants.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/server/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/server/app.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/services/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/services/bag_service.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/services/factory.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/telemetry/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/telemetry/client.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/src/topicforge/tools/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/conftest.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/fixtures/csv_echo_imu.txt +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/fixtures/csv_echo_pose_multi.txt +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/integration/__init__.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/integration/test_real_bus.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_analyze_bag_multi_format.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_bag_service.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_cdr_decoder.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_config.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_dds_introspection.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_dds_schemas.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_dust_adapter.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_fast_adapter.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_inspector.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_lifecycle_buffer.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_live_adapter_parse.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_live_adapter_subprocess.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_metrics_buffer.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_opendds_adapter.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_peek_bag_samples.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_telemetry.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_topic_metrics.py +0 -0
- {topicforge-0.5.4 → topicforge-0.5.5}/tests/test_xtypes.py +0 -0
|
@@ -7,6 +7,127 @@ and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.5] - 2026-10-02
|
|
11
|
+
|
|
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.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
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.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
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),
|
|
71
|
+
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.
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
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.
|
|
116
|
+
|
|
117
|
+
### Known limits
|
|
118
|
+
|
|
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
|
|
126
|
+
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.
|
|
130
|
+
|
|
10
131
|
## [0.5.4] - 2026-10-02
|
|
11
132
|
|
|
12
133
|
First run of the DDS code against a live multi-vendor bus (Windows 11, a
|
|
@@ -1114,7 +1235,8 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
|
|
|
1114
1235
|
- The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
|
|
1115
1236
|
- `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
|
|
1116
1237
|
|
|
1117
|
-
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.
|
|
1238
|
+
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...HEAD
|
|
1239
|
+
[0.5.5]: https://github.com/yaniswav/TopicForge/compare/v0.5.4...v0.5.5
|
|
1118
1240
|
[0.5.4]: https://github.com/yaniswav/TopicForge/compare/v0.5.3...v0.5.4
|
|
1119
1241
|
[0.5.3]: https://github.com/yaniswav/TopicForge/compare/v0.5.2...v0.5.3
|
|
1120
1242
|
[0.5.2]: https://github.com/yaniswav/TopicForge/compare/v0.5.1...v0.5.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: topicforge
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
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
|
|
@@ -69,7 +69,7 @@ Description-Content-Type: text/markdown
|
|
|
69
69
|
|
|
70
70
|
A read-only MCP (Model Context Protocol) server that lets an AI agent inspect a ROS2 graph, recorded bag files and the DDS layer underneath ROS, without being able to publish to the bus or command a robot. It is read-only by **architecture**, not by configuration: there is no write path to misconfigure and no permission system to audit.
|
|
71
71
|
|
|
72
|
-
Without grounding, an LLM asked about a robot will invent topic names, message types and bag contents. TopicForge gives it **
|
|
72
|
+
Without grounding, an LLM asked about a robot will invent topic names, message types and bag contents. TopicForge gives it **twelve typed tools** that return frozen Pydantic schemas, identical whether the server talks to a real robot or to its built-in mock fixtures. It is aimed at ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
|
|
73
73
|
|
|
74
74
|
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. Every conformant vendor announces itself there, so a Cyclone participant also sees RTI Connext, OpenDDS, CoreDX and Dust DDS endpoints without any proprietary binding. This covers discovery only: participants, readers, writers and their QoS. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md).
|
|
75
75
|
|
|
@@ -101,7 +101,7 @@ Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code:
|
|
|
101
101
|
|
|
102
102
|
## Tools
|
|
103
103
|
|
|
104
|
-
All
|
|
104
|
+
All twelve tools are read-only. Every response except `health_check` carries `mode_effective` (`"live"` or `"mock"`), so a caller can tell a real graph from fixtures.
|
|
105
105
|
|
|
106
106
|
| Tool | Purpose |
|
|
107
107
|
| ----------------------- | ------------------------------------------------------------------------------------------------ |
|
|
@@ -116,6 +116,7 @@ All eleven tools are read-only. Every response except `health_check` carries `mo
|
|
|
116
116
|
| `participant_events` | Timeline of participant `discovered` / `lost` events |
|
|
117
117
|
| `topic_metrics` | Frequency, sequence-gap and latency schema; data only for builtin discovery topics |
|
|
118
118
|
| `peek_bag_samples` | Decoded samples from a recorded bag (needs `pip install topicforge[bags]`) |
|
|
119
|
+
| `list_endpoints` | DDS writers and readers with structured QoS, per-topic roll-up that flags orphans (writer with no reader, reader with no writer) |
|
|
119
120
|
|
|
120
121
|
Walkthroughs against the mock, each with the exact tool calls and payloads, are in [`examples/`](examples/README.md). To run the DDS tools against a real bus with several programs and vendors, see [`examples/dds/README.md`](examples/dds/README.md) (`python examples/dds/run_all.py`).
|
|
121
122
|
|
|
@@ -136,7 +137,7 @@ pip install topicforge[dds] # Eclipse CycloneDDS ([dds-cycl
|
|
|
136
137
|
TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
`TOPICFORGE_DDS_BACKEND` accepts `mock` (default), `cyclone`, `fast` and `auto` (`fast`, then `cyclone`, then `mock`, whichever binding imports). An explicit value is honoured with or without `ros2` on PATH, in any mode except `mock`. If the binding is missing or the participant cannot start, the server logs a warning naming the cause and falls back to the ROS2 CLI alone, or to the mock fixtures. When both `ros2` and a DDS backend are up, a composite adapter routes the five ROS2 graph and bag tools to the CLI and the
|
|
140
|
+
`TOPICFORGE_DDS_BACKEND` accepts `mock` (default), `cyclone`, `fast` and `auto` (`fast`, then `cyclone`, then `mock`, whichever binding imports). An explicit value is honoured with or without `ros2` on PATH, in any mode except `mock`. If the binding is missing or the participant cannot start, the server logs a warning naming the cause and falls back to the ROS2 CLI alone, or to the mock fixtures. When both `ros2` and a DDS backend are up, a composite adapter routes the five ROS2 graph and bag tools to the CLI and the seven DDS tools to the DDS backend.
|
|
140
141
|
|
|
141
142
|
A Fast DDS adapter exists but has never run against a bus, and its `fastdds` Python binding is not on PyPI: build it from eProsima's sources and install it next to TopicForge. There is no `[dds-fast]` extra. `opendds` and `dust` are permanent stubs that never serve. `rti`, `opensplice`, `coredx` and `intercom` are rejected with a configuration error, since the Pro tier is retired (see [`docs/pro.md`](docs/pro.md)). Full backend selection, the routing table and the QoS mismatch scenario are in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md); error messages are in [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md).
|
|
142
143
|
|
|
@@ -156,9 +157,12 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
|
|
|
156
157
|
## Limitations
|
|
157
158
|
|
|
158
159
|
- **DDS validation is partial.** The Cyclone adapter has run against a real bus, with Cyclone and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`). The Fast DDS adapter has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed by this project. The multi-vendor claim rests on the RTPS protocol guarantee, not on a recorded cross-vendor run.
|
|
159
|
-
- **User-topic payloads are not decoded.** `peek_dds_samples` on a user topic
|
|
160
|
+
- **User-topic payloads are not decoded.** `peek_dds_samples` on a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read. `topic_metrics` therefore has data only for the builtin discovery topics and says so in its `status`. It is a discovery-layer probe, not a publish-rate monitor.
|
|
161
|
+
- **Liveliness at runtime is not observed.** A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned for 0.5.6. A crash and a clean leave cannot be told apart, and `lost_ns` is an upper bound of the death.
|
|
160
162
|
- **Cyclone vendor ids.** Participants that do not follow the RTPS vendor-id convention in their GUID prefix (Dust DDS, and RTI by default) are reported with vendor `unknown`.
|
|
161
|
-
- **
|
|
163
|
+
- **Single domain.** The server observes the domain it joined at startup; changing it needs a restart.
|
|
164
|
+
- **DDS Security is not handled.** A participant without credentials sees an empty secure bus. `detect_qos_mismatches` checks Partition, type name, Reliability, Durability, Deadline, Liveliness, LatencyBudget, Ownership (kind), DestinationOrder and DataRepresentation (History as a risk); Presentation, XTypes assignability and runtime behavior are not checked, and the result lists them in `policies_unchecked`. It returns a `MismatchScan` envelope: read `reports` for the mismatches.
|
|
165
|
+
- **Fast DDS** serves no `list_endpoints`.
|
|
162
166
|
- **`sample_messages` (live)** runs `ros2 topic echo --csv --once` with a short timeout; a topic with no current publisher returns an empty sample. `timestamp_ns` is the message `header.stamp` for `Header`-stamped types and `0` for headerless ones.
|
|
163
167
|
- **`analyze_bag` (live)** parses `ros2 bag info` text and does not use `rosbags`; anomaly detection is mock-only. `peek_bag_samples` is the only tool that reads the file itself, through `rosbags`, and is served only by the ROS2 CLI adapter or the mock. Without `ros2`, bag tools return fixtures: check `health_check` for `mode: "mock"` before trusting bag output.
|
|
164
168
|
- **Synchronous handlers.** The tools run on the MCP event loop; on Windows a hung `ros2` launcher can block the server.
|
|
@@ -180,10 +184,10 @@ When on, each tool call emits one event with exactly six fields:
|
|
|
180
184
|
|
|
181
185
|
| Field | Example | Notes |
|
|
182
186
|
| ------------ | --------------- | ----------------------------------------------------------- |
|
|
183
|
-
| `tool_name` | `"list_topics"` | One of the
|
|
187
|
+
| `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
|
|
184
188
|
| `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
|
|
185
189
|
| `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
|
|
186
|
-
| `version` | `"0.5.
|
|
190
|
+
| `version` | `"0.5.5"` | TopicForge server version |
|
|
187
191
|
| `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
|
|
188
192
|
| `success` | `true` | Whether the handler returned or raised |
|
|
189
193
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
A read-only MCP (Model Context Protocol) server that lets an AI agent inspect a ROS2 graph, recorded bag files and the DDS layer underneath ROS, without being able to publish to the bus or command a robot. It is read-only by **architecture**, not by configuration: there is no write path to misconfigure and no permission system to audit.
|
|
12
12
|
|
|
13
|
-
Without grounding, an LLM asked about a robot will invent topic names, message types and bag contents. TopicForge gives it **
|
|
13
|
+
Without grounding, an LLM asked about a robot will invent topic names, message types and bag contents. TopicForge gives it **twelve typed tools** that return frozen Pydantic schemas, identical whether the server talks to a real robot or to its built-in mock fixtures. It is aimed at ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
|
|
14
14
|
|
|
15
15
|
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. Every conformant vendor announces itself there, so a Cyclone participant also sees RTI Connext, OpenDDS, CoreDX and Dust DDS endpoints without any proprietary binding. This covers discovery only: participants, readers, writers and their QoS. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md).
|
|
16
16
|
|
|
@@ -42,7 +42,7 @@ Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code:
|
|
|
42
42
|
|
|
43
43
|
## Tools
|
|
44
44
|
|
|
45
|
-
All
|
|
45
|
+
All twelve tools are read-only. Every response except `health_check` carries `mode_effective` (`"live"` or `"mock"`), so a caller can tell a real graph from fixtures.
|
|
46
46
|
|
|
47
47
|
| Tool | Purpose |
|
|
48
48
|
| ----------------------- | ------------------------------------------------------------------------------------------------ |
|
|
@@ -57,6 +57,7 @@ All eleven tools are read-only. Every response except `health_check` carries `mo
|
|
|
57
57
|
| `participant_events` | Timeline of participant `discovered` / `lost` events |
|
|
58
58
|
| `topic_metrics` | Frequency, sequence-gap and latency schema; data only for builtin discovery topics |
|
|
59
59
|
| `peek_bag_samples` | Decoded samples from a recorded bag (needs `pip install topicforge[bags]`) |
|
|
60
|
+
| `list_endpoints` | DDS writers and readers with structured QoS, per-topic roll-up that flags orphans (writer with no reader, reader with no writer) |
|
|
60
61
|
|
|
61
62
|
Walkthroughs against the mock, each with the exact tool calls and payloads, are in [`examples/`](examples/README.md). To run the DDS tools against a real bus with several programs and vendors, see [`examples/dds/README.md`](examples/dds/README.md) (`python examples/dds/run_all.py`).
|
|
62
63
|
|
|
@@ -77,7 +78,7 @@ pip install topicforge[dds] # Eclipse CycloneDDS ([dds-cycl
|
|
|
77
78
|
TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
78
79
|
```
|
|
79
80
|
|
|
80
|
-
`TOPICFORGE_DDS_BACKEND` accepts `mock` (default), `cyclone`, `fast` and `auto` (`fast`, then `cyclone`, then `mock`, whichever binding imports). An explicit value is honoured with or without `ros2` on PATH, in any mode except `mock`. If the binding is missing or the participant cannot start, the server logs a warning naming the cause and falls back to the ROS2 CLI alone, or to the mock fixtures. When both `ros2` and a DDS backend are up, a composite adapter routes the five ROS2 graph and bag tools to the CLI and the
|
|
81
|
+
`TOPICFORGE_DDS_BACKEND` accepts `mock` (default), `cyclone`, `fast` and `auto` (`fast`, then `cyclone`, then `mock`, whichever binding imports). An explicit value is honoured with or without `ros2` on PATH, in any mode except `mock`. If the binding is missing or the participant cannot start, the server logs a warning naming the cause and falls back to the ROS2 CLI alone, or to the mock fixtures. When both `ros2` and a DDS backend are up, a composite adapter routes the five ROS2 graph and bag tools to the CLI and the seven DDS tools to the DDS backend.
|
|
81
82
|
|
|
82
83
|
A Fast DDS adapter exists but has never run against a bus, and its `fastdds` Python binding is not on PyPI: build it from eProsima's sources and install it next to TopicForge. There is no `[dds-fast]` extra. `opendds` and `dust` are permanent stubs that never serve. `rti`, `opensplice`, `coredx` and `intercom` are rejected with a configuration error, since the Pro tier is retired (see [`docs/pro.md`](docs/pro.md)). Full backend selection, the routing table and the QoS mismatch scenario are in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md); error messages are in [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md).
|
|
83
84
|
|
|
@@ -97,9 +98,12 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
|
|
|
97
98
|
## Limitations
|
|
98
99
|
|
|
99
100
|
- **DDS validation is partial.** The Cyclone adapter has run against a real bus, with Cyclone and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`). The Fast DDS adapter has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed by this project. The multi-vendor claim rests on the RTPS protocol guarantee, not on a recorded cross-vendor run.
|
|
100
|
-
- **User-topic payloads are not decoded.** `peek_dds_samples` on a user topic
|
|
101
|
+
- **User-topic payloads are not decoded.** `peek_dds_samples` on a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read. `topic_metrics` therefore has data only for the builtin discovery topics and says so in its `status`. It is a discovery-layer probe, not a publish-rate monitor.
|
|
102
|
+
- **Liveliness at runtime is not observed.** A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned for 0.5.6. A crash and a clean leave cannot be told apart, and `lost_ns` is an upper bound of the death.
|
|
101
103
|
- **Cyclone vendor ids.** Participants that do not follow the RTPS vendor-id convention in their GUID prefix (Dust DDS, and RTI by default) are reported with vendor `unknown`.
|
|
102
|
-
- **
|
|
104
|
+
- **Single domain.** The server observes the domain it joined at startup; changing it needs a restart.
|
|
105
|
+
- **DDS Security is not handled.** A participant without credentials sees an empty secure bus. `detect_qos_mismatches` checks Partition, type name, Reliability, Durability, Deadline, Liveliness, LatencyBudget, Ownership (kind), DestinationOrder and DataRepresentation (History as a risk); Presentation, XTypes assignability and runtime behavior are not checked, and the result lists them in `policies_unchecked`. It returns a `MismatchScan` envelope: read `reports` for the mismatches.
|
|
106
|
+
- **Fast DDS** serves no `list_endpoints`.
|
|
103
107
|
- **`sample_messages` (live)** runs `ros2 topic echo --csv --once` with a short timeout; a topic with no current publisher returns an empty sample. `timestamp_ns` is the message `header.stamp` for `Header`-stamped types and `0` for headerless ones.
|
|
104
108
|
- **`analyze_bag` (live)** parses `ros2 bag info` text and does not use `rosbags`; anomaly detection is mock-only. `peek_bag_samples` is the only tool that reads the file itself, through `rosbags`, and is served only by the ROS2 CLI adapter or the mock. Without `ros2`, bag tools return fixtures: check `health_check` for `mode: "mock"` before trusting bag output.
|
|
105
109
|
- **Synchronous handlers.** The tools run on the MCP event loop; on Windows a hung `ros2` launcher can block the server.
|
|
@@ -121,10 +125,10 @@ When on, each tool call emits one event with exactly six fields:
|
|
|
121
125
|
|
|
122
126
|
| Field | Example | Notes |
|
|
123
127
|
| ------------ | --------------- | ----------------------------------------------------------- |
|
|
124
|
-
| `tool_name` | `"list_topics"` | One of the
|
|
128
|
+
| `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
|
|
125
129
|
| `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
|
|
126
130
|
| `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
|
|
127
|
-
| `version` | `"0.5.
|
|
131
|
+
| `version` | `"0.5.5"` | TopicForge server version |
|
|
128
132
|
| `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
|
|
129
133
|
| `success` | `true` | Whether the handler returned or raised |
|
|
130
134
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# DDS quickstart
|
|
2
|
+
|
|
3
|
+
A short tour of TopicForge's DDS observability tools. The server joins the bus as a **read-only DDS-RTPS participant** and, by the OMG protocol guarantee, observes every conformant vendor through the builtin discovery topics. The `MiddlewareAdapter` protocol has no write method, so the MCP client cannot publish back on any backend. This guide does not assume ROS2.
|
|
4
|
+
|
|
5
|
+
**Validation status.** The Cyclone adapter has run against a live bus, with a Python / Cyclone participant and Dust DDS participants in Rust, Python and C, on Windows and in CI (Ubuntu and Windows, `.github/workflows/demo.yml`); see [`scripts/integration/README.md`](../scripts/integration/README.md). The Fast DDS adapter has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed yet. For runnable live scenarios (who is on the bus, why two nodes cannot talk, a node that crashed, a late joiner that misses data), see [`examples/dds/README.md`](../examples/dds/README.md). Multi-vendor positioning: [`dds-interop-matrix.md`](dds-interop-matrix.md).
|
|
6
|
+
|
|
7
|
+
## 1. Mock mode
|
|
8
|
+
|
|
9
|
+
The mock fixtures expose every tool with no DDS SDK:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install topicforge
|
|
13
|
+
TOPICFORGE_MODE=mock python -m topicforge
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- `list_participants(domain_id=0)` returns four participants: two CycloneDDS (`mock-robot`, `mock-laptop`), one Fast DDS (`mock-aerospace-node`) and one Dust DDS in Rust (`mock-rust-node`). The `vendor` field comes from the OMG vendor id: `cyclone`, `fast`, `rti`, `rti_micro`, `opensplice`, `opendds`, `coredx`, `intercom`, `dust`, `mock` or `unknown`.
|
|
17
|
+
- `detect_qos_mismatches(topic=None)` returns a `MismatchScan` with one report for `/dds/qos_mismatch`: a RELIABLE reader against a BEST_EFFORT writer.
|
|
18
|
+
- `list_endpoints()` returns the mock writers and readers with their QoS and a `by_topic` roll-up.
|
|
19
|
+
- `peek_dds_samples(topic="/dds/well_matched", count=3)` returns three deterministic samples. The mock only knows `/dds/well_matched`, `/dds/qos_mismatch`, `/dds/ddsforge/example` and `/dds/ddsforge/opaque`, and raises "Unknown DDS topic" for anything else.
|
|
20
|
+
- `topic_metrics(topic="/dds/heartbeat_10hz", window_seconds=60)` returns a pre-filled 10 Hz buffer (100 samples, no gaps, 50 ms latency). A live adapter behaves differently, see section 5 and [`examples/04-monitor-topic-frequency.md`](../examples/04-monitor-topic-frequency.md).
|
|
21
|
+
|
|
22
|
+
The mock illustrates payload shapes that no live adapter produces today, such as the `"full"` decode status on `/dds/ddsforge/example`.
|
|
23
|
+
|
|
24
|
+
## 2. Live mode: choose a backend
|
|
25
|
+
|
|
26
|
+
**Cyclone** is the one with a PyPI install path:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install topicforge[dds] # same as [dds-cyclone]
|
|
30
|
+
TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`cyclonedds` publishes wheels for CPython 3.10 to 3.13 on Windows, Linux and macOS (as of 11.0.1). A background thread reads the builtin DCPS topics through `BuiltinDataReader`. On a real bus `list_participants` reports each participant's `name` (EntityName QoS) and `hostname` (the `__Hostname` discovery property) when the remote participant sets them, and the vendor from the first two bytes of the GUID prefix; implementations that do not follow that convention (Dust DDS, RTI by default) show vendor `unknown`.
|
|
34
|
+
|
|
35
|
+
**Fast DDS** has no PyPI install path. The `fastdds` binding is not published, so TopicForge declares no extra for it. Build eProsima's [Fast-DDS-python](https://github.com/eProsima/Fast-DDS-python) from source (it needs the Fast DDS C++ libraries and SWIG), install it into the same environment, then `TOPICFORGE_DDS_BACKEND=fast`. The adapter was written against the 2.6.x API and has never run against a bus.
|
|
36
|
+
|
|
37
|
+
**Auto** (`TOPICFORGE_DDS_BACKEND=auto`) probes importable bindings in the order `fast`, `cyclone`, `mock`. With only the PyPI extra installed it resolves to Cyclone. `opendds` and `dust` are permanent stubs that always report unavailable and are not in the chain.
|
|
38
|
+
|
|
39
|
+
**Domain.** `TOPICFORGE_DDS_DOMAIN_ID` (`0..232`, default `0`) is joined at startup; the `domain_id` tool parameter exists for protocol uniformity only. Changing domains needs a restart.
|
|
40
|
+
|
|
41
|
+
**Commercial vendors.** `rti`, `opensplice`, `coredx` and `intercom` are rejected at startup with a configuration error. You do not need them to observe an RTI bus; for what a native RTI adapter would add (secure domains with vendor credentials, shared-memory-only deployments) see [`pro.md`](pro.md).
|
|
42
|
+
|
|
43
|
+
## 3. The QoS mismatch scenario
|
|
44
|
+
|
|
45
|
+
The canonical "my subscriber does not receive" case. Against the mock:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
> Detect QoS mismatches on the current bus.
|
|
49
|
+
|
|
50
|
+
[tool call: detect_qos_mismatches]
|
|
51
|
+
{
|
|
52
|
+
"reports": [
|
|
53
|
+
{
|
|
54
|
+
"topic": "/dds/qos_mismatch",
|
|
55
|
+
"reader_guid": "...", "writer_guid": "...",
|
|
56
|
+
"reader_participant_name": "lidar_driver", "writer_participant_name": "nav_planner",
|
|
57
|
+
"incompatible_policies": ["Reliability"],
|
|
58
|
+
"severity": "incompatible",
|
|
59
|
+
"details": [{"policy": "Reliability", "requested": "RELIABLE",
|
|
60
|
+
"offered": "BEST_EFFORT", "rule": "a RELIABLE reader needs a RELIABLE writer"}],
|
|
61
|
+
"mode_effective": "mock"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"not_matched": [],
|
|
65
|
+
"hints": ["Topic '/dds/ddsforge/opaque' has writers but no reader: there is no pair to compare."],
|
|
66
|
+
"pairs_checked": 3, "topics_scanned": 4,
|
|
67
|
+
"policies_checked": ["Reliability", "Durability", "Deadline", "Liveliness", "..."],
|
|
68
|
+
"policies_unchecked": ["Presentation: ...", "..."],
|
|
69
|
+
"mode_effective": "mock"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The result is a `MismatchScan` envelope. Live adapters render GUIDs in dotted form (`xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx`). From this the agent can suggest a concrete fix: the writer is BEST_EFFORT but the reader requires RELIABLE, so relax the reader or upgrade the writer. The analysis is vendor-neutral pure code (`adapters/common/qos_analyzer.py`, `qos_scan.py`) over canonical `QosProfile` models, so it does not depend on which backend produced the discovery samples.
|
|
74
|
+
|
|
75
|
+
Order of checks per reader/writer pair: Partition first (wildcards `*` and `?` on one side match; wildcard against wildcard never does), then the type name, then the RxO policies. A pair separated by partition or type goes to `not_matched` and no QoS rule is run on it, so a partition split is never reported as a Reliability problem; an empty `reports` with a non-empty `not_matched` still means no data flows. RxO policies compared: Reliability, Durability, Deadline, Liveliness (kind and lease), LatencyBudget, Ownership (kind only), DestinationOrder, DataRepresentation. History (KEEP_ALL reader, KEEP_LAST writer) is reported as `risky`, not as an incompatibility. A policy a side did not announce is skipped and counted in a hint. `policies_unchecked` lists what stays out of scope (Presentation, XTypes assignability, runtime liveliness, anything not discoverable): discovery shows declared QoS, not runtime behavior, so an empty result is not proof the bus is healthy.
|
|
76
|
+
|
|
77
|
+
## 4. Composite adapter and backend selection
|
|
78
|
+
|
|
79
|
+
When `ros2` is on PATH and a DDS backend starts, TopicForge builds both a `Ros2CliAdapter` and the DDS adapter behind a `CompositeAdapter`: the five ROS2 graph and bag tools go to the CLI, the seven DDS tools to the DDS backend. An explicit DDS backend (`cyclone`, `fast`, `auto`) is honoured in every mode except `mock`, including on a host without `ros2`.
|
|
80
|
+
|
|
81
|
+
| `TOPICFORGE_MODE` | `TOPICFORGE_DDS_BACKEND` | `ros2` on PATH | Active adapter | ROS2 graph and bag tools | DDS tools |
|
|
82
|
+
| --- | --- | --- | --- | --- | --- |
|
|
83
|
+
| `mock` | any | any | `MockAdapter` | fixtures | fixtures |
|
|
84
|
+
| `live` / `auto` | `mock` (default) | yes | `Ros2CliAdapter` | CLI | raise "DDS module is not active" |
|
|
85
|
+
| `live` / `auto` | `cyclone` / `fast` / `auto` | yes | `CompositeAdapter` | CLI | real binding |
|
|
86
|
+
| `live` / `auto` | `cyclone` / `fast` / `auto` | no | the DDS adapter alone | raise "DDS observability only" | real binding |
|
|
87
|
+
| `live` / `auto` | `cyclone` / `fast`, binding missing or participant fails | any | `Ros2CliAdapter` if `ros2` is on PATH, else `MockAdapter` | CLI, or fixtures | raise, or fixtures |
|
|
88
|
+
| `live` / `auto` | `mock` | no | `MockAdapter` | fixtures | fixtures |
|
|
89
|
+
|
|
90
|
+
A DDS problem never prevents startup: the server logs a warning naming the cause and keeps going. Only an invalid or removed configuration value stops it. `health_check` reports what was actually built: `mode` (can differ from `requested_mode`), `ros_backend` (`ros2_cli`, `mock`, `none`) and `dds_backend` (`cyclone`, `fast`, `mock`, `none`).
|
|
91
|
+
|
|
92
|
+
## 5. Scope of the discovery tools
|
|
93
|
+
|
|
94
|
+
`list_endpoints` returns every announced writer and reader as a typed record: role, topic, type, owning participant (guid, name, vendor), structured QoS and announcement time. Its `by_topic` roll-up flags orphans (`no_reader`, `no_writer`) and lists endpoints of participants that left under `departed_writers` / `departed_readers`. TopicForge's own endpoints are excluded unless `include_observer` is set. Cyclone and the mock serve it; the Fast DDS backend raises "not supported yet". Use it first to find out who talks on which topic, and `detect_qos_mismatches` to learn why they do not match.
|
|
95
|
+
|
|
96
|
+
`peek_dds_samples` is structured on the three builtin discovery topics, with both backends. Builtin names carry no leading `/`:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
peek_dds_samples(topic="DCPSParticipant", count=5)
|
|
100
|
+
peek_dds_samples(topic="DCPSSubscription", count=10)
|
|
101
|
+
peek_dds_samples(topic="DCPSPublication", count=10)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Each sample is the cached current discovery state, not a stream. The payload carries `vendor`, `guid`, `topic_name` and, for endpoints, `role`, `participant_guid`, `participant_name`, `type_id`, `qos`, `announced_ns` and `is_observer`. The sample `timestamp_ns` is the announcement time.
|
|
105
|
+
|
|
106
|
+
**User topics are not decoded.** For a user topic the tool returns count 0 and a `note`:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"topic": "/my/topic",
|
|
111
|
+
"count": 0,
|
|
112
|
+
"samples": [],
|
|
113
|
+
"mode_effective": "live",
|
|
114
|
+
"note": "payload decoding is disabled for DDS user topics, so no samples are returned; this does not mean the topic is silent. Use list_endpoints for the topic's presence, writers, readers and QoS"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
An empty result says nothing about traffic. The earlier decode path never worked on either backend and is disabled until it can be validated against a real bus. The `"full"` and `"partial"` decode statuses stay in the schema and the mock emits examples of them, but no live adapter produces them.
|
|
119
|
+
|
|
120
|
+
**`topic_metrics` only has data for the builtin topics.** For a user topic it returns `status="unsupported_user_topic"`, and its null fields are not a measurement. For a builtin topic, `frequency_hz_observed` is how often you called `peek_dds_samples` (one call yields `null`), `sequence_numbers_available` is `false` and the latency percentiles are `null`, because builtin samples carry no publish timestamp. `frequency_hz_declared` is `1 / deadline` for the shortest Deadline a writer announced, when there is one. Use it to watch discovery-layer churn, not to check a publish rate.
|
|
121
|
+
|
|
122
|
+
**Lifecycle.** On Cyclone a background thread (0.5 s period) reads the three builtin discovery topics and feeds the caches behind every discovery tool, so `participant_events` and `list_participants` do not depend on when you call them. A participant that cycles faster than the discovery history depth between two passes can still be missed. Event times come from DDS (`announced_ns`, `lost_ns`), with `time_source` saying which clock. A `lost` time is an upper bound of the death: a crash is only noticed when the lease expires, and a crash cannot be told from a clean leave. A writer that is alive but silent is not observable without reading its data. Fast DDS captures arrival and removal through listener callbacks.
|
|
123
|
+
|
|
124
|
+
## 6. Open work
|
|
125
|
+
|
|
126
|
+
Wider real-bus validation (Fast DDS, RTI, OpenDDS, CoreDX, OpenSplice; re-enabling user-topic decoding depends on it), an opt-in data probe to catch a hung writer (planned for 0.5.6), and DDS Security, which is not handled at all: a participant without credentials sees an empty secure bus. The roadmap is in [`product-plan.md`](product-plan.md). Errors and fixes: [`TROUBLESHOOTING.md`](TROUBLESHOOTING.md). Report what you see on a real domain at https://github.com/yaniswav/TopicForge/issues.
|
|
@@ -4,7 +4,7 @@ How to get a working ROS2 environment to point TopicForge at, and how to wire it
|
|
|
4
4
|
|
|
5
5
|
| You want to... | Time | Path |
|
|
6
6
|
| --- | --- | --- |
|
|
7
|
-
| Try the
|
|
7
|
+
| Try the twelve tools without installing ROS2 | 5 min | [Mock mode](#mock-mode) |
|
|
8
8
|
| Live mode on Windows | 45 min | [WSL2 + Humble](#wsl2--ros2-humble-windows-recommended) |
|
|
9
9
|
| Live mode on Ubuntu | 20 min | [Linux native](#linux-native) |
|
|
10
10
|
| Throwaway environment | 15 min | [Docker](#docker) |
|
|
@@ -102,4 +102,4 @@ TopicForge speaks MCP over stdio; any compliant client can spawn it with a comma
|
|
|
102
102
|
}
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
That is the Claude Desktop shape (`claude_desktop_config.json`); restart the app and the
|
|
105
|
+
That is the Claude Desktop shape (`claude_desktop_config.json`); restart the app and the twelve tools appear under the hammer icon. For Claude Code run `claude mcp add topicforge -- topicforge`. Cursor, Continue and Cline accept the same stdio config. If the `topicforge` script is not on PATH, use `"command": "python", "args": ["-m", "topicforge"]`, or the absolute path of the binary inside your venv: desktop clients do not inherit your shell's PATH or venv activation.
|
|
@@ -31,7 +31,7 @@ You called a ROS2 graph or bag tool (`list_topics`, `get_topic_info`, `sample_me
|
|
|
31
31
|
2. Re-run with `TOPICFORGE_MODE=live` and your existing `TOPICFORGE_DDS_BACKEND`.
|
|
32
32
|
3. Check `health_check`: `ros_backend` should be `"ros2_cli"` and `dds_backend` your vendor.
|
|
33
33
|
|
|
34
|
-
If you want a DDS-only deployment, the message is expected: you use the
|
|
34
|
+
If you want a DDS-only deployment, the message is expected: you use the seven DDS tools and the two bag tools raise it. For offline work use `TOPICFORGE_MODE=mock`. The opposite error, "DDS module is not active", means `TOPICFORGE_DDS_BACKEND` is `mock` (the default) while a live adapter serves; set it to `cyclone`.
|
|
35
35
|
|
|
36
36
|
## "rosbags"
|
|
37
37
|
|
|
@@ -31,7 +31,7 @@ Add it to your MCP client. For Claude Desktop, edit `claude_desktop_config.json`
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Restart Claude Desktop. All
|
|
34
|
+
Restart Claude Desktop. All twelve tools appear under the hammer icon. Ask something like:
|
|
35
35
|
|
|
36
36
|
> What topics are being published right now, and what message types do they carry?
|
|
37
37
|
|
|
@@ -25,7 +25,7 @@ Notice: a Rust implementation is in this list. That is the OMG-DDS promise: lang
|
|
|
25
25
|
|
|
26
26
|
When you install TopicForge with DDS support (`pip install topicforge[dds]`, which installs the CycloneDDS Python binding), it joins the domain you point it at as a read-only participant. The Fast DDS adapter works the same way, but its Python binding is not on PyPI: you build it from eProsima's sources (see [`DDS_QUICKSTART.md`](DDS_QUICKSTART.md)).
|
|
27
27
|
|
|
28
|
-
From there, TopicForge's discovery-based tools (`list_participants`, `detect_qos_mismatches`, `participant_events`, and `peek_dds_samples` on the builtin `DCPS*` topics) see **every conformant participant on the bus**, regardless of:
|
|
28
|
+
From there, TopicForge's discovery-based tools (`list_participants`, `list_endpoints`, `detect_qos_mismatches`, `participant_events`, and `peek_dds_samples` on the builtin `DCPS*` topics) see **every conformant participant on the bus**, regardless of:
|
|
29
29
|
|
|
30
30
|
- **The vendor**: RTI Connext, OpenDDS, CoreDX, Fast DDS, Cyclone, InterCOM, Dust DDS, or any other DDS-RTPS conformant stack
|
|
31
31
|
- **The host language**: C, C++11/14/17/20, Rust, Java, .NET, Python, Ada, anything with a binding
|
|
@@ -35,7 +35,7 @@ The two known interop gaps from the 2025-05 OMG report (Dust DDS <-> OpenDDS, Du
|
|
|
35
35
|
|
|
36
36
|
## Limits of this claim
|
|
37
37
|
|
|
38
|
-
The claim is about **discovery**: which participants, readers and writers exist, and what QoS they announce. It does not extend to user-topic payloads: `peek_dds_samples`
|
|
38
|
+
The claim is about **discovery**: which participants, readers and writers exist, and what QoS they announce. It does not extend to user-topic payloads: `peek_dds_samples` on a user topic returns count 0 and a note, and does not decode contents, for any vendor; `list_endpoints` shows the topic's writers, readers and QoS. The project's own real-bus runs cover Cyclone and Dust DDS participants only (see [`../scripts/integration/README.md`](../scripts/integration/README.md)); RTI, OpenDDS, CoreDX, OpenSplice and Fast DDS have not been observed. For the rest the claim follows from the RTPS standard and from the OMG's published results above. Vendors that do not follow the RTPS vendor-id convention in the GUID prefix (Dust DDS, RTI by default) are reported with vendor `unknown` on Cyclone. Domains that use DDS Security are not observable at all, because TopicForge joins without credentials.
|
|
39
39
|
|
|
40
40
|
## What TopicForge is not
|
|
41
41
|
|
|
@@ -22,7 +22,7 @@ Integration and adaptation to a specific ROS2 / DDS environment, native RTI Conn
|
|
|
22
22
|
|
|
23
23
|
## Known limitations
|
|
24
24
|
|
|
25
|
-
DDS Security is not implemented on any adapter: if your domain requires authenticated or encrypted RTPS, TopicForge cannot join it today. `detect_qos_mismatches` covers Reliability, Durability,
|
|
25
|
+
DDS Security is not implemented on any adapter: if your domain requires authenticated or encrypted RTPS, TopicForge cannot join it today. `detect_qos_mismatches` covers Partition, type name, Reliability, Durability, Deadline, Liveliness, LatencyBudget, Ownership (kind), DestinationOrder and DataRepresentation, with History as a risk; Presentation, XTypes assignability and runtime behavior are not checked.
|
|
26
26
|
|
|
27
27
|
## Contact
|
|
28
28
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
TopicForge is **the safety-first read-only MCP for ROS2 robotics**. Where general-purpose ROS-MCP servers let an LLM publish topics, call services, and command robots (useful for demos, untenable for production fleets, defense systems, or anything safety-certified), TopicForge 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.
|
|
10
10
|
|
|
11
|
-
Concretely, the server exposes **
|
|
11
|
+
Concretely, the server exposes **twelve typed read-only tools today** (v0.5.5): the five ROS2-graph tools (`health_check`, `list_topics`, `get_topic_info`, `sample_messages`, `analyze_bag`) plus the seven DDS / observability tools shipped across v0.2.0-v0.5.5 (`list_participants`, `list_endpoints`, `detect_qos_mismatches`, `peek_dds_samples`, `participant_events`, `topic_metrics`, `peek_bag_samples`). They are backed by a deterministic mock adapter (no ROS2/DDS required), a `ros2` CLI wrapper, or an OSS DDS participant (Eclipse CycloneDDS / eProsima Fast DDS). Outputs are frozen Pydantic schemas, stable across runtime modes. Telemetry is opt-in, six fields, zero user payload.
|
|
12
12
|
|
|
13
13
|
The ROS-MCP category is no longer empty (see section 11 Risk register for the competitive landscape as of 2026-05-13). What TopicForge defends, and the rest of the pack will inherit, is the read-only-by-architecture stance and the production-quality engineering envelope around it: frozen schemas, mock-first development, telemetry contract pinned by tests, Windows-first cross-platform, no shell injection, deterministic outputs.
|
|
14
14
|
|
|
@@ -46,7 +46,7 @@ The strategic bet is **pack breadth via two focused products plus a modular surf
|
|
|
46
46
|
|
|
47
47
|
**Motif of the pivot.** Earlier drafts of this plan sequenced a 3-to-5-MCP pack with a separate DDS observability MCP as MCP 02. The 2026-05-14 audit collapsed that into a 2-product strategy: TopicForge as an umbrella covering both middlewares, DatasetForge as the second standalone product. The binding constraints were (a) solo-maintenance cost of running two repos in parallel and (b) the fact that ROS2 and DDS are the same problem shape (a typed pub/sub graph that needs structured introspection) and the `RosAdapter` protocol already generalizes to a `MiddlewareAdapter` superset with zero rework. Two products instead of three reduces the surface area without losing coverage.
|
|
48
48
|
|
|
49
|
-
The umbrella commits TopicForge to a broader scope than first drafted: the DDS module shipped **six** DDS / observability tools across v0.2.0-v0.4.0 (not the three originally scoped), taking the surface to **11 tools total
|
|
49
|
+
The umbrella commits TopicForge to a broader scope than first drafted: the DDS module shipped **six** DDS / observability tools across v0.2.0-v0.4.0 (not the three originally scoped), taking the surface to **11 tools total**, then **12** with `list_endpoints` (0.5.5, approved 2026-10-02 after a blind evaluation). The original 8-tool ceiling was formally revised: see the re-scope decision in section 11. The pack inherits the layer separation, mock-first development, opt-in telemetry, and read-only-by-architecture commitments from TopicForge. Pack-shared infrastructure extraction (telemetry, license, settings resolver into a `pack-template/` repo) becomes a non-decision at 2 products: fork-and-tweak from TopicForge to DatasetForge is acceptable ; revisit only if a third product is ever planned.
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
@@ -160,7 +160,7 @@ The risks worth tracking explicitly. Updated 2026-09-05 (previous pass: 2026-05-
|
|
|
160
160
|
- **Cross-platform regressions on Windows -- CLOSED (2026-09-05).** Was: CI tested `ubuntu-latest` only, with Windows coverage manual-only before each release. Resolved in v0.5.0: `.github/workflows/ci.yml`'s matrix now runs `{ubuntu-latest, windows-latest} x {3.10, 3.11, 3.12, 3.13}` (3.10 added in 0.5.3, verified current). Residual, smaller risk: the Makefile still uses POSIX shell syntax, so users on plain PowerShell run the underlying commands listed in the README "Development" section.
|
|
161
161
|
- **Telemetry trust.** Even opt-in telemetry can damage trust if the payload contract drifts. Mitigation: `tests/test_telemetry.py::test_payload_contains_only_whitelisted_keys` pins the six allowed keys. Any change requires a CHANGELOG entry and a README Telemetry section update in the same PR.
|
|
162
162
|
- **Time / focus dilution.** A solo maintainer trying to drive two products (TopicForge umbrella + DatasetForge), commercial-support work for each, marketing, and the DDS module on top of TopicForge is the realistic risk. The 2026-05-14 pivot from a 3-to-5-MCP pack to a 2-product strategy reduced the surface but did not eliminate the risk. Mitigation: explicit phase gates (do not start Phase 2 until Phase 1 is shipped, do not act on the DDS module marketing until Phase 2 has shipped), though section 8 schedules `MiddlewareAdapter` protocol prep during Phase 1.
|
|
163
|
-
- **Scope creep within the TopicForge umbrella.** Combining ROS2 + DDS introspection in one product risks bloating the tool surface beyond what a focused MCP should expose. **Re-scope decision (2026-07-08, ratified retroactively).** The register's original ceiling (5 ROS2 tools + at most 3 DDS tools, any 9th tool gated on a re-scope discussion documented *here* before code lands) was crossed during v0.4.0 **without that discussion being recorded in this register**, a governance gap surfaced by the 2026-07-08 external audit. The three tools that broke it are deliberate and were acknowledged in the CHANGELOG and `docs/projet-file/mcp-02-spec.md section 2` at ship time: `participant_events` (9th, v0.4.0 Phase 1), `topic_metrics` (10th, Phase 2), `peek_bag_samples` (11th, Phase 3). They are accepted; the revised ceiling
|
|
163
|
+
- **Scope creep within the TopicForge umbrella.** Combining ROS2 + DDS introspection in one product risks bloating the tool surface beyond what a focused MCP should expose. **Re-scope decision (2026-07-08, ratified retroactively).** The register's original ceiling (5 ROS2 tools + at most 3 DDS tools, any 9th tool gated on a re-scope discussion documented *here* before code lands) was crossed during v0.4.0 **without that discussion being recorded in this register**, a governance gap surfaced by the 2026-07-08 external audit. The three tools that broke it are deliberate and were acknowledged in the CHANGELOG and `docs/projet-file/mcp-02-spec.md section 2` at ship time: `participant_events` (9th, v0.4.0 Phase 1), `topic_metrics` (10th, Phase 2), `peek_bag_samples` (11th, Phase 3). They are accepted; the revised ceiling was **11 tools** and moved to **12** on 2026-10-02 for `list_endpoints`. A 13th tool now needs an explicit re-scope discussion documented in this register before code lands. Mitigation going forward: the `verify-change` skill's doc-drift step and the `docs-curator` sweep keep this register, `README.md`, and `CLAUDE.md` in sync so a ceiling break cannot ship undocumented again.
|
|
164
164
|
|
|
165
165
|
---
|
|
166
166
|
|
|
@@ -17,6 +17,6 @@ TOPICFORGE_MODE=mock python -m topicforge
|
|
|
17
17
|
# Windows PowerShell: $env:TOPICFORGE_MODE="mock"; python -m topicforge
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Point any MCP client at the server with `"env": { "TOPICFORGE_MODE": "mock" }`. The mock exposes all
|
|
20
|
+
Point any MCP client at the server with `"env": { "TOPICFORGE_MODE": "mock" }`. The mock exposes all 12 tools against deterministic fixtures, so every example is reproducible byte for byte.
|
|
21
21
|
|
|
22
22
|
To run against a real bus, use `TOPICFORGE_MODE=live` and, for the DDS tools, `TOPICFORGE_DDS_BACKEND=cyclone` (see [`docs/DDS_QUICKSTART.md`](../docs/DDS_QUICKSTART.md)). The mock shows the shape of every response, not the behaviour of the live DDS adapters, which differ in two ways that examples 02 and 04 call out: user-topic payloads are not decoded, and `topic_metrics` only has data for the builtin discovery topics.
|
|
@@ -27,7 +27,9 @@ python run.py --hold # keep the programs running, ask your own MCP client
|
|
|
27
27
|
```
|
|
28
28
|
[1] Which reader/writer pairs can never talk?
|
|
29
29
|
-> detect_qos_mismatches
|
|
30
|
+
odom: matched (declared QoS): writer nav_planner -> reader lidar_driver
|
|
30
31
|
scan: Reliability (incompatible): writer lidar_driver -> reader nav_planner
|
|
32
|
+
Reliability: reader asks RELIABLE, writer offers BEST_EFFORT
|
|
31
33
|
|
|
32
34
|
[3] What does nav_planner actually receive on scan?
|
|
33
35
|
-> nav_planner output
|
|
@@ -36,8 +38,8 @@ python run.py --hold # keep the programs running, ask your own MCP client
|
|
|
36
38
|
|
|
37
39
|
[4] What does lidar_driver actually receive on odom?
|
|
38
40
|
-> lidar_driver output
|
|
39
|
-
[lidar_driver] rx odom: 10 in 1.0 s, last seq
|
|
40
|
-
[lidar_driver] rx odom: 10 in 1.0 s, last seq
|
|
41
|
+
[lidar_driver] rx odom: 10 in 1.0 s, last seq 50
|
|
42
|
+
[lidar_driver] rx odom: 10 in 1.0 s, last seq 60
|
|
41
43
|
```
|
|
42
44
|
|
|
43
45
|
- On `scan`, the planner **requests** RELIABLE delivery and the driver only
|
|
@@ -45,7 +47,8 @@ python run.py --hold # keep the programs running, ask your own MCP client
|
|
|
45
47
|
offers, so DDS refuses the match.
|
|
46
48
|
- On `odom` the QoS differ the other way: the writer offers RELIABLE and the
|
|
47
49
|
reader asks for BEST_EFFORT. That is allowed (offering more than asked is
|
|
48
|
-
fine), so TopicForge does not report it.
|
|
50
|
+
fine), so TopicForge does not report it as a mismatch. It lists it under
|
|
51
|
+
`matched`, the pairs DDS will connect on the declared QoS.
|
|
49
52
|
|
|
50
53
|
## Ask your agent
|
|
51
54
|
|
|
@@ -28,21 +28,22 @@ python run.py --hold # keep the programs running, ask your own MCP client
|
|
|
28
28
|
[2] The LIDAR driver crashes. How long until the bus notices?
|
|
29
29
|
-> list_participants
|
|
30
30
|
killed lidar_driver
|
|
31
|
-
lidar_driver reported as left after
|
|
31
|
+
lidar_driver reported as left after 10 s
|
|
32
32
|
|
|
33
33
|
[3] What happened on the bus, in order?
|
|
34
34
|
-> participant_events
|
|
35
35
|
lost lidar_driver
|
|
36
|
-
discovered lidar_driver
|
|
37
|
-
discovered nav_planner
|
|
38
36
|
discovered topicforge
|
|
37
|
+
discovered nav_planner
|
|
38
|
+
discovered lidar_driver
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
- Cyclone DDS uses a 10 s lease by default
|
|
42
|
-
|
|
43
|
-
- TopicForge
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
- Cyclone DDS uses a 10 s lease by default, so the departure is reported when
|
|
42
|
+
the lease expires.
|
|
43
|
+
- TopicForge tracks discovery in the background, so the event is there when
|
|
44
|
+
you ask, and its time comes from DDS rather than from your question. The
|
|
45
|
+
`lost` time is an upper bound of the death: after a crash it is the lease
|
|
46
|
+
expiry, and a crash looks the same as a clean leave.
|
|
46
47
|
- The lease is a per-vendor default you can configure. Dust DDS, for
|
|
47
48
|
example, uses 100 s: a crashed Dust program stays "active" much longer.
|
|
48
49
|
|