TopicForge
TopicForge is a read-only MCP server for inspecting ROS2 graphs, DDS discovery/QoS, and recorded bags without any write/publish ability.
Check health, effective mode, ROS/DDS backend availability, domain, sample caps.
List ROS2 topics and get per-topic type, publisher/subscriber counts, QoS reliability/durability.
Sample recent ROS2 messages with count/array controls.
Analyze ROS2 bags (.mcap/.db3/rosbag2 dirs), and peek decoded bag samples including ROS1 .bag.
List DDS participants and their lifecycle events (discovered/lost).
List DDS endpoints (writers/readers) with QoS, participant info, and orphan detection per topic.
Detect QoS mismatches between DDS readers/writers (reliability, durability, deadline, partitions, etc.).
Peek raw DDS samples on builtin discovery topics; user-topic payload decoding is disabled.
Get temporal DDS topic metrics (frequency, sequence gaps, latency) where supported.
Run in mock, live, or auto mode with deterministic fixtures for demos/CI.
Cannot publish, command a robot, alter QoS, or write to bags/bus.
Provides read-only inspection of ROS2 graphs, topics, QoS policies, and recorded bag files. Enables AI agents to discover ROS2 topics, sample messages, analyze .mcap/.db3 recordings, inspect DDS participants and endpoints, and debug QoS mismatches between readers and writers.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@TopicForgewhy isn't /scan reaching nav_planner? check for QoS mismatches"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
TopicForge
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. The code has no write path: it cannot publish to the bus or command a robot, and there is no permission system to configure.
Every tool also declares itself read-only in the MCP protocol (readOnlyHint, destructiveHint=false, idempotentHint, with an honest openWorldHint), and a test fails if any tool lacks it. See SECURITY.md for the guarantee and its limits.
It gives the agent sixteen typed tools that return frozen Pydantic schemas (output contract 2: one object per tool, one note, units in the field names; see docs/CONTRACT.md), identical whether the server talks to a real robot or to its built-in mock fixtures. Ask why nav_planner gets no scan, and the agent reads the bus, finds the BEST_EFFORT writer facing a RELIABLE reader and names the incompatible policy (see examples/02-debug-qos-mismatch.md). It is meant for ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. Cyclone DDS, Fast DDS and Dust DDS participants have been observed on live buses: the Fast DDS ones on every CI run of the ROS 2 test bench (Humble and Jazzy with rmw_fastrtps_cpp) and in the OmniSim validation. RTI Connext, OpenDDS and CoreDX announce themselves through the same standard discovery, but none of them has been observed yet. This covers discovery only: participants, readers, writers and their QoS. See docs/dds-interop-matrix.md.
Quickstart
No ROS2 is needed; the mock adapter serves deterministic fixtures for a small differential robot (LIDAR + RGB camera). Python 3.10 to 3.13.
pip install topicforge
TOPICFORGE_MODE=mock python -m topicforge
# Windows PowerShell: $env:TOPICFORGE_MODE="mock"; python -m topicforgeThe server speaks MCP over stdio and waits for a client, so wire it into one. For Claude Desktop, add to claude_desktop_config.json:
{
"mcpServers": {
"topicforge": {
"command": "python",
"args": ["-m", "topicforge"],
"env": { "TOPICFORGE_MODE": "auto" }
}
}
}Then ask it to list the topics or to analyze /tmp/demo.mcap. For Claude Code: claude mcp add topicforge -- topicforge. If the client cannot launch the server (WSL2 to Windows, a robot over an SSH tunnel), topicforge --transport streamable-http --port 8765 serves http://127.0.0.1:8765/mcp on loopback only; see docs/CLIENTS.md.
Other MCP clients. Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in docs/CLIENTS.md. One-click: Add to Cursor | Install in VS Code.
Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in docs/TESTING.md; recurring monitoring prompts and the privacy contract are in docs/TUTORIEL.md.
Related MCP server: ros2-mcp
Tools
Every response except health_check carries mode_effective ("live" or "mock"), so a caller can tell a real graph from fixtures, and a single note that explains anything unusual. health_check.contract_version is 2. The full reference, with every field, is generated in docs/TOOLS.md; upgrading from 0.6.x: docs/MIGRATION-0.7.md.
Tool | Purpose |
| Environment and mode introspection. Always succeeds; reports |
| Discover the ROS2 graph |
| Message type, publisher/subscriber counts and QoS for one topic |
| ROS 2 nodes on the graph: names, namespaces, duplicate names (one |
| One node's publishers, subscribers, services, actions, parameters (secrets masked) and |
| The TF tree from |
| What changed since it last worked: snapshots the ROS 2 graph (or a bag) and diffs it with |
| Peek recent messages on a ROS2 topic (count capped at 50) |
| Summarize a |
| DDS participants on the domain: vendor, |
| Incompatible QoS pairs between DDS readers and writers |
| Raw DDS samples; structured on the three builtin discovery topics, presence-only on user topics |
| Timeline of participant |
| Frequency, sequence-gap and latency schema; data only for builtin discovery topics |
| Decoded samples from a recorded bag, including ROS 1 |
| DDS writers and readers with structured QoS, per-topic roll-up that flags orphans (writer with no reader, reader with no writer) |
The server also offers three MCP prompts, diagnose-dds-bus, inspect-ros2-robot and diagnose-nav2, that give a client the read-only call order, and short server instructions for clients that do not show prompts (see docs/CLIENTS.md).
Walkthroughs against the mock, each with the exact tool calls and payloads, are in examples/. To run the DDS tools against a real bus with several programs and vendors, see examples/dds/README.md (python examples/dds/run_all.py).
Modes
Mode | When to use | Backend |
| Development, demos, CI, screencasts | Deterministic fixtures |
| ROS2 sourced and on PATH, and/or a DDS backend |
|
| Detect what is available, else mock (default) | Best available |
live and auto degrade instead of failing: if neither ros2 nor a DDS backend comes up, the server serves the mock fixtures and health_check reports mode: "mock" next to requested_mode. Only TOPICFORGE_MODE=mock forces fixtures unconditionally. The live ROS2 adapter shells out to the ros2 CLI, so rclpy does not need to be importable.
DDS backends
pip install topicforge # includes Eclipse CycloneDDS where wheels exist (Python 3.10-3.13; Linux x86_64, Windows x64, macOS)
pip install "topicforge[dds]" # alias, kept so older commands work; forces a source build elsewhere (needs the Cyclone C library)
TOPICFORGE_DDS_BACKEND=cyclone python -m topicforgeTOPICFORGE_DDS_BACKEND accepts mock (default), cyclone, fast and auto (fast, then cyclone, then mock, whichever binding imports). The default mock selects no DDS backend: installing the Cyclone binding is not enough, you must also set TOPICFORGE_DDS_BACKEND=cyclone. With TOPICFORGE_MODE=live and no backend selected, the DDS tools raise DDS module is not active: ... with the actual cause (backend not selected, binding not installed, or binding installed but the adapter failed to start), and health_check reports dds_backend: "none" plus dds_inactive_note. 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 eight ros2 CLI tools (list_topics, get_topic_info, list_nodes, get_node_info, inspect_tf, graph_diff, sample_messages, analyze_bag) to the CLI and the six DDS tools to the DDS backend; peek_bag_samples reads bags without the CLI.
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. Any other value (rti, opensplice, coredx, intercom, opendds, dust) is rejected at startup with an error that lists the valid ones; Cyclone already sees those vendors' participants through standard discovery. Full backend selection, the routing table and the QoS mismatch scenario are in docs/DDS_QUICKSTART.md; error messages are in docs/TROUBLESHOOTING.md.
Configuration reference
Variable | Default | Description |
|
|
|
|
|
|
|
| Name or path of the ROS2 CLI binary |
|
| Opt-in anonymous telemetry; an unrecognized value aborts startup. See Telemetry |
|
|
|
|
| DDS domain observed (0..232). Joined at startup; changing it needs a restart |
|
| Size cap for one sampled message (1 KiB..64 MiB); a call returns at most 4 times that. Over-cap messages are dropped with a note |
Command-line options (not environment variables): --transport stdio|streamable-http (default stdio) and --port N (default 8765, only with streamable-http). The HTTP transport binds 127.0.0.1 only, with DNS-rebinding protection and no authentication; see docs/CLIENTS.md.
Samples with comments are in .env.example. Any invalid value stops the server with topicforge: configuration error: ... and exit code 2, so a typo cannot silently change behaviour.
Limitations
External validation against a simulated robot's ground truth (OmniSim): docs/VALIDATION.md.
DDS validation is partial. The Cyclone adapter has run against real buses with Cyclone, Fast DDS and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (
.github/workflows/demo.yml,.github/workflows/ros2-live.yml). The Fast DDS adapter (TopicForge joining through thefastddsbinding) 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.User-topic payloads are not decoded.
peek_dds_sampleson a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read.topic_metricstherefore has data only for the builtin discovery topics and says so in itsstatus. It is a discovery-layer probe, not a publish-rate monitor.Liveliness at runtime is not observed. A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned. A crash and a clean leave cannot be told apart, and
lost_nsis an upper bound of the death.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.Single domain: the server observes the domain it joined at startup; changing it needs a restart.
DDS Security is not handled. A participant without credentials sees an empty secure bus.
detect_qos_mismatcheschecks 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 inpolicies_unchecked. It returns aMismatchScanenvelope: readreportsfor the mismatches.Fast DDS serves no
list_endpoints.sample_messages(live) streamsros2 topic echountilcountmessages arrive ortimeout_s(1..40, default 10, for the whole call) runs out, and returns what arrived with anote(N of M messages within T s, saying whether a publisher exists). QoS is matched to the publishers, so latched topics work. The payload has nested named fields (payload.header.stamp.sec);timestamp_nsisheader.stamp(the publisher's clock, sim time on a simulation), the time in the body of/clock,/tfand/rosoutmessages (stamp_sourcepayload), or 0 for other headerless types, withstamp_sourceandreceived_ns(wall clock when the CLI printed it). Arrays are cut at 128 elements by default;max_array_length(1..65536, or null for no cut) andarrays_summary_onlychange that, and a cut is listed under_truncated_fields.nanandinfcome back as strings.countabove 50 is capped with a note.analyze_bag(live) parsesros2 bag infotext for the totals and counts; anomaly detection is mock-only. Per-topic times, rates ((n - 1) / span) andlatchedare added when the bag can be read locally (.db3with the standard library,.mcapwithrosbags), else rates fall back to count / bag duration (frequency_basis).peek_bag_samplesreads the file itself, throughrosbags, returns the firstcountmessages in recording order (not the last), and is served only by the ROS2 CLI adapter or the mock; itstimestamp_nsis the message'sheader.stamp(stamp_sourceheader), the body time ofClock,TFMessageandLogmessages (payload) or, without either, the bag record time (stamp_sourcerecorded), andrecorded_nsis always the bag record time; bags that embed no message definitions (Humble.db3) are decoded with the Humble definitions, or the distro the bag records, andnotesays so. Withoutros2, bag tools return fixtures: checkhealth_checkformode: "mock"before trusting bag output.health_checkalso reportssim_clock_published(live only): true when/clockhas a publisher, a hint that header stamps may be simulation time.Calls are serialized per backend (one
ros2call at a time, one DDS call at a time) and each call has a 45 s wall budget, lock wait included; a call that cannot get its lane fails at once withbusy.health_checkandpeek_bag_samplestake no lock.No streaming or push subscriptions: tools are strictly request/response.
Next: an opt-in probe to tell a hung writer from a healthy one, wider real-bus validation (Fast DDS, RTI, OpenDDS), and DDS Security. Open work is tracked in issues.
Telemetry
Opt-in, anonymous and off by default. When off, instrumentation returns the handler unchanged: no event is built, no transport is constructed, no network code runs (pinned by tests/test_telemetry.py::test_build_app_off_makes_no_transport_calls).
TOPICFORGE_TELEMETRY=on python -m topicforgeOn-values: on, 1, true, yes, enabled. Off-values: unset, off, 0, false, no, disabled. Anything else is a configuration error rather than a silent "off".
When on, each tool call emits one event with exactly six fields:
Field | Example | Notes |
|
| One of the sixteen tools, never argument values |
|
| Handler wall-clock duration, 2 decimals |
|
| Mode of the adapter actually serving: |
|
| TopicForge server version |
|
| Random UUID per process, never persisted |
|
| Whether the handler returned or raised |
Never sent: topic names, message types or payloads, bag paths or contents, hostnames, usernames, IP addresses, environment variables, error messages. The field set is fenced by tests/test_telemetry.py::test_payload_contains_only_whitelisted_keys; adding a field requires updating this section. The default transport is a structured log line; there is no HTTP endpoint yet. The implementation is in src/topicforge/telemetry/.
Security model
TopicForge is designed for local trust: it runs as a subprocess of your MCP client on a machine you control and inspects your own ROS2 graph, DDS domain and bag files. It is not hardened for adversarial inputs.
TOPICFORGE_ROS2_BINaccepts an arbitrary path; treat it the way you treatPATH.analyze_bagandpeek_bag_samplesopen whatever path the client passes (no workspace isolation, no symlink restriction).All
ros2invocations usesubprocess.runwith an argument list, nevershell=True. ROS2 topic names are validated against^/[A-Za-z0-9_/]+$first.The server loads no third-party code at startup.
No outbound network calls unless telemetry is turned on.
Before exposing TopicForge to untrusted MCP clients (hosted endpoints, shared environments), add path isolation and revisit the TOPICFORGE_ROS2_BIN policy. Vulnerability reports: see SECURITY.md.
Development
git clone https://github.com/yaniswav/TopicForge.git && cd TopicForge
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -e ".[dev]"
python -m ruff check src tests
python -m pytestTests run against the mock adapter, the live adapter's pure parsers and the binding-free DDS helpers; they never need a running ROS graph. Tests needing the cyclonedds or fastdds binding skip themselves when it is absent, and the integration marker (real-bus tests) is deselected by default. The Makefile (make check) uses POSIX shell syntax; on plain PowerShell run the commands above. See CONTRIBUTING.md.
Upgrading
TopicForge is pre-1.0; CHANGELOG.md lists every change, including the yanked releases and removed extras. 0.7.0 changed the shape of tool results (contract_version 2): docs/MIGRATION-0.7.md maps every old field to its new one.
Layout
src/topicforge/
server/ MCP bootstrap, build_app(settings), prompts and instructions
tools/ thin MCPServer handlers, no backend logic
services/ input validation, orchestration, adapter factory
adapters/ ros2_live, ros2_mock, dds_cyclone, dds_fast, common/ (binding-free logic)
models/ frozen Pydantic schemas, the contract with MCP clients
config/ settings and mode resolution
telemetry/ opt-in, off by default
examples/ mock walkthroughs (*.md) and runnable live DDS examples (dds/)
scripts/ real-bus interop checks
docs/ guides, product plan
tests/ pytest suite, mock-only, no ROS2 or DDS requiredLayers are strictly separated: handlers never call subprocess, adapters are the only code that talks to a backend, and new backends implement the MiddlewareAdapter protocol in adapters/base.py.
License
MIT, see LICENSE. Integration or support work for a specific ROS 2 / DDS setup: ethvignot.yanis@gmail.com.
Available Tools
16 toolsanalyze_bagARead-onlyIdempotent
Summarize a ROS 2 bag at path as a BagAnalysis: storage format, duration, message count, per-topic stats and mode_effective. Per topic frequency_hz is (n - 1) / (last - first message time) (frequency_basis topic_span), with first_timestamp_ns, last_timestamp_ns and latched; a latched topic whose messages all fall within 1 s (e.g. /tf_static) has a null frequency_hz. When the bag cannot be read locally or is a .mcap over 200 MiB, the rate falls back to count / bag duration (bag_duration) and note says why. Live mode runs ros2 bag info on .mcap, .db3 or rosbag2_* directories (for ROS 1 .bag use peek_bag_samples); mock mode returns fixture data for any bag-like path, and only mock detects anomalies. Raises an MCP error for a malformed or missing path (live), unparseable output, or without a ros2 CLI. Read-only; no side effects. Duration: ros2 CLI behind the ROS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Bag path: a `.mcap` or `.db3` file or a `rosbag2_*` directory (`peek_bag_samples` also reads ROS 1 `.bag`; `analyze_bag` does not). Existence and format are checked live; mock accepts any well-formed path. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Why the result is less detailed than usual (e.g. rates over the whole bag duration); else `None`. |
| path | Yes | Bag path as supplied. |
| topics | Yes | Per-topic statistics for every topic present in the bag. |
| start_ns | No | Record time in ns of the bag's first message (rosbag2 `starting_time`): the zero of `peek_bag_samples` `start_s` / `end_s`. `null` when unreadable. |
| anomalies | No | Notes on gaps or clock jumps; mock mode only. |
| bag_format | No | Detected container: `mcap`, `db3`, `bag` or `unknown`; `None` for `ros2 bag info` text. |
| duration_s | Yes | Bag duration in seconds. |
| message_count | Yes | Total number of messages across all recorded topics. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| storage_format | No | `mcap`, `sqlite3`, or other storage identifier when known. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/no-destructive, and the description still adds substantial behavior: the 200 MiB `.mcap` fallback rule, the `bag_duration` basis and `note`, which error conditions raise MCP errors, the 45 s ceiling, and single-call lock serialization. The only small gap is that the `latched`/1 s rule is described operationally without saying how the caller should interpret a null `frequency_hz`.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the return shape, then falls back to computation semantics, modes, error cases and timing — a sensible information hierarchy with no filler sentences. It is dense to the point of being a wall of bolded clauses, which costs a point on scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, the description still covers modes, format gate, fallback math, error surface, lock contention and timeout — everything needed to call it correctly and to interpret a degraded result. Nothing material is left for the agent to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the parameter is already self-documented, so the baseline is 3; the description nonetheless adds that existence and format are validated live while mock accepts any well-formed path, which is meaning beyond the schema. It largely restates the accepted extension list, which caps it below 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ('Summarize a ROS 2 bag at `path`') and names the concrete return type (`BagAnalysis`) plus its fields, so the agent knows exactly what it gets back. It also routes ROS 1 `.bag` users to `peek_bag_samples`, distinguishing it from the closest sibling in the same sentence.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly splits live mode (`.mcap`, `.db3`, `rosbag2_*`) from mock mode (any bag-like path, anomaly detection only) and states the alternative (`peek_bag_samples`) for the format this tool rejects. It even gives retry guidance for the `busy` lock error.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detect_qos_mismatchesARead-onlyIdempotent
Explain why DDS readers and writers on a topic do not talk, and who will. Pairs every reader with every writer per topic and returns a MismatchScan: summary (one line, read it first), matched (pairs DDS will connect given the announced QoS; data flow is not observed), reports (incompatible or risky QoS pairs, with participant names, requested vs offered values and the failed rule in details), not_matched (pairs DDS never matches: different partitions or type names; the QoS rules are NOT evaluated for them, and latent_incompatible_policies lists what would also break once fixed), hints (probable typos in orphan topic names, type id notes), plus pairs_checked, topics_scanned, policies_checked and policies_unchecked. Checked: Partition (* and ? wildcards), type name, Reliability, Durability, Deadline, Liveliness, LatencyBudget, Ownership, DestinationOrder, DataRepresentation, History (risky only, where announced). An empty reports with a non-empty not_matched still means no data flows, and an all-empty result does not prove the bus healthy: discovery shows the QoS DECLARED, not runtime behavior (a 'deadline missed' log with compatible QoS means the writer's real period exceeds the deadline, which TopicForge cannot see). Pass topic to scope the scan. The lists are capped at 200 each (truncated; *_total keep the counts). A late_joiner match is a VOLATILE writer whose reader joined later on the same host: normal. Reports and partition splits carry suggested_fixes: text for a human, never applied by TopicForge; verify_with names the call that confirms the repair. Read-only by architecture. Raises an MCP error when no DDS module is active; mock returns fixtures. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | Optional DDS topic to scope the scan to: `scan` or `rt/scan`. Omit to scan all topics. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| hints | Yes | Leads that are not findings: probable typos, type id notes, incomplete checks. |
| matched | No | Pairs DDS will connect given the announced QoS (data flow is not observed). |
| reports | Yes | Pairs sharing partition and type with incompatible or risky QoS. |
| summary | No | One-line reading of the scan: counts of incompatible, not matched and matched pairs. |
| truncated | No | True when `reports`, `matched` or `not_matched` hit the 200-entry cap (see `*_total`); narrow with `topic`. |
| not_matched | Yes | Pairs DDS never matches (partition or type name). Non-empty with empty `reports` is NOT all-clear: no data flows between them. |
| matched_total | No | Matched pairs before the size cap. |
| pairs_checked | Yes | Same-topic (reader, writer) pairs examined. |
| reports_total | No | Reports before the size cap. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| topics_scanned | Yes | Topics that had at least one endpoint in scope. |
| policies_checked | Yes | Policies compared on every pair. |
| not_matched_total | No | Not-matched pairs before the size cap. |
| policies_unchecked | Yes | What the scan does not cover, each with a reason; a clean result says nothing about them. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds plenty beyond annotations: read-only by architecture, raises an MCP error without an active DDS module, runs under a DDS lock with a 45 s bound and `busy` retry, caps lists at 200 with `truncated`/`*_total`, and flags that discovery shows declared QoS rather than runtime behaviour. It also explains that `late_joiner` matches are normal and that suggested fixes are text only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the returned scan fields, then the caveats and error/latency notes. It is dense and long, and some content (read-only, return shape) duplicates annotations or the output schema, but nearly every sentence carries operational value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for a scan tool with an output schema: it covers scoping, truncation, lock/duration behaviour, error conditions, and the important false-negative caveat about empty results. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter, and the schema already documents `topic` at 100% coverage, including the omit-to-scan-all behaviour. The description repeats this rather than adding syntax or format details, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource: explains why DDS readers and writers on a topic cannot communicate and reports who can. It clearly differs from siblings like get_topic_info or list_endpoints by describing a pairwise QoS compatibility scan with a named return type.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Says to pass `topic` to scope the scan, and that omitting it scans all topics. It also points at `verify_with` for confirming repairs and warns that an empty `reports` still means no data flows. It does not explicitly state when to prefer a sibling such as get_topic_info, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_node_infoARead-onlyIdempotent
ROS 2 graph only; on a DDS-only setup use list_participants. Describe one ROS 2 node as a NodeInfo: the topics it publishes and subscribes to, the services and actions it serves or calls (each {name, type}), its parameters and use_sim_time. Interfaces come from the graph (ros2 node info), which never waits for the node. Parameters come from a read (ros2 param dump) bounded by timeout_s: a node that does not answer in time is a finding, not an error: parameters is null, parameters_note says its executor is probably blocked, and the interfaces are still returned. A value whose name contains password, secret, token, api_key or credential is replaced by <masked> and a long value is cut (truncated, original_size), both said in parameters_note. With params_file, param_diff lists declared values that differ on the node, are declared_only or masked. With several nodes of that name duplicate_count is above 1 and note warns. Raises an MCP error when the node is not on the graph (the message lists close matches), the name is malformed, or without a ros2 CLI. Read-only: the only request TopicForge ever sends is a parameter read (list/get); it sends no other request to a node. Duration: ros2 CLI behind the ROS lock, one call at a time; up to three CLI calls (node list and info, 8 s each at most; the parameter read, at most timeout_s, 20 s); ends within 45 s, less time for the read after a wait; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| node | Yes | Fully qualified node name as `list_nodes` gives it, e.g. `/robot1/lidar_driver` (a missing leading `/` is added). | |
| timeout_s | No | Seconds to wait for the parameter read, 1..20, default 8. A node that does not answer is reported in `parameters_note`, not as an error. | |
| params_file | No | Server path of a ROS 2 parameter `.yaml` (1 MiB max). |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| full_name | Yes | Fully qualified node name, e.g. `/lidar_driver`. |
| lifecycle | No | True when `service_servers` has `<full_name>/get_state` (GetState). |
| param_diff | No | File against node: differences only. |
| parameters | No | Parameters sorted by name; `None` when unreadable (see `parameters_note`); an empty list means none. |
| publishers | Yes | Topics the node publishes. |
| subscribers | Yes | Topics the node subscribes to. |
| use_sim_time | No | Value of the node's `use_sim_time` parameter. `None` when unknown: see `use_sim_time_note`. |
| action_clients | Yes | Actions the node calls. |
| action_servers | Yes | Actions the node serves. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| duplicate_count | Yes | Nodes using this exact full name; above 1, the data above belongs to one of them. |
| param_diff_note | No | |
| parameters_note | No | Why `parameters` is `None`, or what was masked or cut; `None` when complete. |
| service_clients | Yes | Services the node calls. |
| service_servers | Yes | Services the node offers, its own parameter services included. |
| use_sim_time_note | No | Why `use_sim_time` is `None`. `None` when it is set. |
| param_diff_summary | No | Counts left out: same, live_only. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive), yet the description adds substantial non-obvious behavior: a timed-out parameter read is a finding rather than an error (partial result with `parameters_note`), secrets matching password/secret/token/api_key/credential are replaced with `<masked>`, long values are truncated, and duplicate node names raise `duplicate_count`. The duration/lock model (up to three CLI calls, 8 s each, 45 s ceiling) is unusually precise and consistent with the read-only annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the sibling alternative before any detail, and nearly every sentence carries operational information. It is dense and heavy with bold emphasis, which costs some readability, but there is no filler to cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter, open-world read tool with an output schema, this covers everything an agent needs: scope, alternative, error conditions, partial-result semantics, masking, time bounds, and concurrency. Nothing required to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantic weight beyond the schema: `timeout_s` governs only the parameter read and a miss is reported rather than raised, and `params_file` is explained by its effect (`param_diff` listing differ / declared_only / masked entries). It does not restate the `node` name format, leaving that to the schema, which is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ("Describe one ROS 2 node as a `NodeInfo`") and immediately enumerates what that record contains: topics, services/actions, parameters, use_sim_time. It is trivially distinguishable from siblings like get_topic_info and list_participants, which the description names as the alternative in the DDS case.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly scopes itself ("ROS 2 graph only") and routes the agent elsewhere when that scope fails ("on a DDS-only setup use `list_participants`"). It also states the failure boundary — an MCP error when the node is absent from the graph, the name is malformed, or no `ros2` CLI exists — and the `busy` retry condition.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_topic_infoARead-onlyIdempotent
ROS 2 graph only; on a DDS-only setup use list_endpoints. Describe one ROS 2 topic (fully qualified, e.g. /cmd_vel) as a TopicInfo: counts and, in live mode, publisher_qos / subscription_qos (reliability, durability where transient_local marks a latched topic such as /tf_static, endpoint_count; a null side says why in its _note) and publisher_nodes / subscriber_nodes. Raises an MCP error for a malformed or unknown topic, or without a ros2 CLI. Read-only; no side effects. Duration: ros2 CLI behind the ROS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI. |
Output Schema
| Name | Required | Description |
|---|---|---|
| name | Yes | |
| note | No | One sentence of context, or `None`. |
| message_type | Yes | |
| publisher_qos | No | QoS of the publishers. `None` when it was not read: see `publisher_qos_note`. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| publisher_count | Yes | |
| publisher_nodes | No | Publisher node names (`ros2 topic info --verbose`); empty when unknown. |
| subscriber_count | Yes | |
| subscriber_nodes | No | Subscriber node names; empty when unknown. |
| subscription_qos | No | QoS of the subscriptions. `None` when it was not read: see `subscription_qos_note`. |
| publisher_qos_note | No | Why `publisher_qos` is `None`. `None` when it is set. |
| subscription_qos_note | No | Why `subscription_qos` is `None`. `None` when it is set. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing the ROS lock, the 45 s bound, the single-call-at-a-time serialization, the `busy`/retry path, and the MCP error conditions (malformed/unknown topic, missing CLI). Safety is already covered by readOnly/idempotent hints, so the only minor gap is that it does not say how counts behave when the topic exists but has no endpoints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the routing constraint and then the operation, with a labeled Duration clause at the end. The QoS field enumeration is dense but earns its place; slightly heavy for one sentence's worth of content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers routing, error modes, timing and lock behavior for a single-parameter read tool with an output schema that already explains the return shape. Only minor omission is what happens for a valid topic with zero endpoints (empty lists vs error).
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already constrains the format, so baseline is 3; the description adds value by restating the fully qualified requirement as a precondition for calling and by noting rejection happens before the CLI is reached, which hints at fast-fail behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb + resource: 'Describe one ROS 2 `topic` ... as a `TopicInfo`', with the fully qualified name format given inline. It distinguishes itself from siblings list_topics (list vs describe) and list_endpoints (ROS 2 graph vs DDS-only) explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with the ROS 2 graph constraint and names the exact alternative for a DDS-only setup (`list_endpoints`). It also states the retry condition on `busy`, so the agent knows what to do rather than giving up.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
graph_diffARead-onlyIdempotent
ROS 2 graph only. Answer "what changed since it last worked": snapshot the graph (or bag_path) and compare it with baseline, as a GraphDiff. changes are node, topic, type, qos, rate and count differences; added = present now, absent from the baseline. not_compared names what a pair cannot support: nodes and counts need live against live, rates bag against bag, writer QoS a DDS backend. /rosout and /parameter_events count noise is only tallied in builtin_changes_count. The first call stores a snapshot and says so in note. Snapshots stay in memory (16, lost on restart). Raises an MCP error for an unknown id, a bad bag path or without a ros2 CLI. Mock mode: the first call reports 3 changes, the next 0. Read-only. Duration: ros2 CLI behind the ROS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| bag_path | No | Snapshot this bag instead of the live graph. | |
| baseline | No | `previous` (last snapshot), a `snap-N` id from `snapshot_ids`, or a bag path. | previous |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| changes | Yes | |
| current | Yes | |
| baseline | No | `null` without baseline. |
| added_count | Yes | |
| baseline_id | No | `null` without baseline (`note`). |
| snapshot_id | Yes | Stored snapshot id. |
| not_compared | Yes | Skipped comparison and why. |
| snapshot_ids | Yes | Held ids, oldest first. |
| changed_count | Yes | |
| removed_count | Yes | |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| builtin_changes_count | Yes | Count changes on `/rosout`, `/parameter_events`: not in `changes`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only/no-destructive annotations, it discloses stateful snapshot storage and its 16-entry in-memory cap, first-call note behavior, MCP errors, mock mode behavior, and lock/duration semantics. These are concrete behavioral traits that materially help an agent invoke it correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded and most detail is useful, but the description is long and explains return fields like changes, added, and not_compared despite an output schema existing. 'Read-only' also largely repeats the annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a stateful diff tool with many edge cases, the description covers live vs bag modes, baseline selection, error conditions, snapshot retention, mock behavior, and timing constraints. With an output schema present, it does not need to explain every return value in depth.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already carries the parameter semantics. The description mirrors bag_path and baseline meanings and adds first-call context, but it does not add much syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific operation (snapshot and compare), resource (ROS 2 graph), and output shape (GraphDiff). It distinguishes the tool from single-resource siblings by focusing on what changed between a current snapshot and a baseline.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly frames the use case as 'what changed since it last worked' and explains live vs bag_path plus baseline options. It also lists pair-wise limitations in not_compared, but it does not explicitly name sibling alternatives for non-diff tasks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
health_checkARead-onlyIdempotent
Report TopicForge environment state as a HealthReport: mode, ros_backend, dds_backend, ros_tools_available, ros2_distro, rmw_implementation, dds_domain_id and observed_domain_note (only the domain joined at startup is observed), server version and sample cap; for a live DDS backend also observer_started_ns / now_ns (nothing earlier was watched) and the discovery tracker status tracker_* (errors, evictions or a stale pass mean gaps). live with ros_backend none is a DDS-only setup: use list_endpoints. With dds_backend none, dds_inactive_note says why. payload_decoding is disabled (DDS user-topic payloads are not decoded); dds_security is not_supported. Always succeeds: call it first when something looks wrong. Read-only; no side effects. Duration: never waits for a lock and never runs the ros2 CLI; it answers at once.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| mode | Yes | Mode serving: `mock` or `live` (`live` with `ros_backend` `none`: DDS tools only); may differ from `requested_mode` after a fallback. |
| note | No | One sentence of context, or `None`. |
| now_ns | No | Server wall-clock time (ns since epoch) when this report was built. |
| rmw_source | No | `env`, `ros2_cli`, `distro_default` (not verified on the graph) or `none`. |
| dds_backend | No | DDS backend serving: `mock`, `cyclone`, `fast`, or `none` (DDS module inactive). |
| ros2_distro | No | `ROS_DISTRO` if set (disclosed by design under the local-trust model). |
| ros_backend | No | ROS 2 backend: `ros2_cli`, `mock` or `none`. |
| dds_security | No | DDS Security is not handled: protected endpoints and data are invisible. |
| dds_domain_id | No | DDS domain id observed when the DDS module is active. |
| requested_mode | Yes | Mode requested by configuration (may be `auto`). |
| ros2_available | Yes | Whether a `ros2` CLI is on PATH. |
| server_version | Yes | TopicForge version. |
| tracker_errors | No | Tracker passes that raised; non-zero means gaps. |
| tracker_passes | No | Completed discovery tracker passes since start. |
| tracker_running | No | Whether the discovery tracker thread is alive (Cyclone); `None` without a tracker. |
| contract_version | No | Output contract version; changes when a field is renamed, removed or retyped. |
| max_sample_count | Yes | Server cap on samples per call; larger requests are clamped. |
| payload_decoding | No | `disabled`: DDS user-topic payloads are not decoded. |
| dds_inactive_note | No | Why `dds_backend` is `none` while ROS 2 serves; else `null`. |
| ros_security_note | No | Set when `ros_security_enabled`: what stays invisible. |
| rmw_implementation | No | ROS 2 middleware, e.g. `rmw_fastrtps_cpp`; `None` when `rmw_source` is `none`. |
| observer_started_ns | No | ns when the DDS observer joined; nothing earlier was watched. `None` without one. |
| ros_tools_available | No | True when the ROS 2 tools can run (`ros_backend` is not `none`); on a DDS-only setup use `list_endpoints`. |
| sim_clock_published | No | Whether `/clock` had a publisher at the last graph read (stamps may be sim time); `None` in mock or without a recent read. |
| middleware_available | No | True when a DDS backend serves; else whether the configured binding is importable. |
| observed_domain_note | No | Which DDS domain is observed: only the one joined at startup; others are invisible. |
| ros_security_enabled | No | `ROS_SECURITY_ENABLE` is `true`; `None` unless the ros2 CLI serves. |
| tracker_last_pass_ns | No | ns of the last tracker pass; far older than `now_ns` means stale lifecycle. |
| payload_decoding_note | No | Reason for `payload_decoding`. |
| ros_security_strategy | No | `ROS_SECURITY_STRATEGY` when set to a valid value. |
| tracker_cache_evictions | No | Discovery entries dropped from a full tracker cache; non-zero means the bus is bigger than listed. |
| ros_security_keystore_set | No | Whether a keystore variable is set (the path is never shown). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, idempotent, non-destructive behavior, and the description adds substantial context beyond that: it always succeeds, never waits for a lock, never runs the ros2 CLI, and answers at once. It also discloses field-level semantics such as observed_domain_note only reflecting the startup domain and tracker_* errors indicating gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is a single front-loaded paragraph with no filler, and the dense field list earns its space by attaching operational meaning to fields like observed_domain_note and tracker_*. The critical advice to call it first is stated clearly at the end.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description does not need to explain return values, but it still adds the failure-mode and field-semantics context an agent needs to interpret the report. It covers the always-succeeds guarantee, DDS-only and no-DDS cases, and the limited payload decoding and security scope.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4 and there is no input schema detail the description needs to compensate for. The description appropriately focuses on the report and its semantics rather than inventing parameter guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: report TopicForge environment state as a HealthReport. It distinguishes the tool from siblings by naming list_endpoints for DDS-only setups and by explaining how health_check differs from a live data-listing/query tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage guidance: call it first when something looks wrong. It also names the relevant alternative, list_endpoints, for the specific case where ros_backend is none, and explains what dds_inactive_note means when dds_backend is none.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
inspect_tfARead-onlyIdempotent
ROS 2 graph only. Rebuild the TF tree from the transforms on /tf_static (latched) and /tf (listened to for window_s) and flag what breaks lookups. Returns a TfReport: frames (parent, kind static/dynamic/both, age_s, stale, observed_frequency_hz on wall time, sim_frequency_hz on stamps), roots, counts and findings {kind, frames, text}: stale (a frame trails the newest /tf stamp by more than stale_after_s), two_parents, disconnected (several roots), static_and_dynamic, cycle, no_path. Ages and rates use header stamps, so they hold on sim time (real_time_factor far from 1 means sim). Give source_frame and target_frame for the path between them. It also reads /robot_description and flags URDF links with no frame (declared_not_published). Empty frames when nothing is published (read note). The listening is capped (3000 messages): echo can drop messages at high rates, so the tree is exact but frequencies are approximate (truncated). Raises an MCP error without a ros2 CLI. Mock mode: a fixed 5 s capture of the demo robot with one stale frame. Duration: ros2 CLI behind the ROS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| window_s | No | Seconds to listen to `/tf`, 1..20, default 5. | |
| source_frame | No | Frame name, e.g. `base_link`. Give `source_frame` and `target_frame` together to get the chain between them in `path`. | |
| target_frame | No | See `source_frame`. | |
| stale_after_s | No | A frame is stale past this many seconds of stamp time, default 2. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| path | No | Frames from `source_frame` to `target_frame`. |
| hints | Yes | |
| roots | Yes | Frames with no parent; one in a sound tree. |
| frames | Yes | Sorted by name. |
| findings | Yes | Empty when the tree is sound. |
| window_s | Yes | |
| path_note | No | Why `path` is `None`, or context. |
| truncated | Yes | Messages were dropped or the cap was hit. |
| urdf_note | No | |
| frame_count | Yes | |
| stale_count | Yes | |
| urdf_source | No | |
| reference_ns | Yes | Newest `/tf` stamp: the clock for `age_s`. |
| stamp_span_s | Yes | |
| static_count | Yes | Frames with a static transform. |
| dynamic_count | Yes | Frames with a dynamic transform. |
| message_count | Yes | Transforms captured, repeats included. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| transform_count | Yes | Distinct parent-child pairs. |
| urdf_link_count | No | |
| window_actual_s | Yes | |
| real_time_factor | Yes | Stamp time over wall time: about 1 on a real robot, other on sim. |
| frames_not_in_urdf | No | TF frames not in the URDF (`map`, `odom` are normal). |
| declared_frames_missing | No | URDF links with no TF frame. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the read-only/idempotent profile, and the description adds substantial operational context beyond that: MCP error without a `ros2` CLI, mock-mode behavior, 45 s duration bound, ROS-lock serialization with a `busy` retry, 3000-message listen cap with `truncated` flag, and wall-time vs stamp-time frequency caveats. It also discloses what empty results mean.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose before the report schema and operational caveats, and nearly every clause carries information. It is a dense wall of text, however, and the TfReport field enumeration partly duplicates the output schema, making it heavier than needed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still names the finding kinds an agent must reason over (stale, two_parents, disconnected, cycle, no_path) and covers failure modes, time-base semantics, and mock behavior. Nothing needed for correct invocation or interpretation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description reinforces that `window_s` applies to `/tf` only (not latched `/tf_static`) and restates `stale_after_s` in stamp-time terms, but adds little that the schema does not already spell out.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Rebuild the TF tree from the transforms on /tf_static and /tf') plus its scope ('ROS 2 graph only'), which separates it from the topic/node/DDS inspection siblings. An agent immediately knows this is the transform-tree analysis tool rather than a generic graph lister.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear context is given: use source_frame+target_frame to get the chain between them, read `note` when `frames` is empty, retry on `busy`. It lacks explicit when-not-to-use guidance or a named alternative, but no sibling overlaps with TF analysis, so the routing risk is low.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_endpointsARead-onlyIdempotent
List every DDS endpoint (writer and reader) announced on the bus as an EndpointListing; one EndpointInfo each with role, dds_topic (raw, e.g. rt/scan), ros_topic (/scan; null with ros_topic_note for a non-ROS topic), type_name, type_id, the owning participant_guid with participant_name, and a structured qos. Use it instead of parsing peek_dds_samples and joining GUID prefixes. Large buses: pass fields compact to replace each qos by a one-line qos_summary and drop type_id, announced_ns and ros_topic_note; the default full is verbose, so scope with topic or participant_guid to read one topic's full QoS. Orphans: by_topic rolls endpoints up per topic with writer_count, reader_count, orphan (no_reader = a writer nobody subscribes to, no_writer = a reader nobody publishes to) and the union of partitions. In qos, a duration or policy of None is infinite/unset or not announced. Among EXCLUSIVE writers the live one with the highest ownership_strength delivers; which one owns an instance is runtime state TopicForge cannot observe. Departed endpoints: a leaving participant's endpoints are remembered (last 200, 1 h) and shown in by_topic as departed_writers / departed_readers (listed in endpoints only with include_departed), so a topic that lost its only writer is explained in one call. Topic filter: rt/scan and scan match each other; a filter matching nothing returns a note with the closest topics. This lists discovery facts, not data flow: it cannot tell a silent or hung writer from a healthy one; pair it with detect_qos_mismatches. The observer's own endpoints are excluded unless include_observer; ROS 2 service and action endpoints (rq/, rr/, rs/, rp/, ra/, ros_discovery_info) unless include_internal. Capped at 500 endpoints (truncated, total); by_topic covers all matches. Read-only. Raises an MCP error when no DDS module is active; mock returns a fixture. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | Only endpoints on this DDS topic: `scan` or `rt/scan` (exact name first, then the alternate form). Omit for every topic. | |
| fields | No | `full` (default) or `compact`: one-line `qos_summary` instead of the full `qos`, no `type_id`, `announced_ns` or `ros_topic_note`. Use `compact` on large buses. | full |
| domain_id | No | Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value. | |
| include_departed | No | Also list endpoints whose participant left (flagged with `gone_ns`); `by_topic` reports them either way. Default false. | |
| include_internal | No | Also list ROS 2 service and action endpoints (`rq/`, `rr/`, `rs/`, `rp/`, `ra/`, `ros_discovery_info`), most of a small robot's endpoints. Default false. | |
| include_observer | No | Include TopicForge's own observer endpoints. Default false. | |
| participant_guid | No | Only endpoints of this participant (a guid from `list_participants`). Omit for all. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Set when a `topic` filter matched nothing: the closest known topics. |
| hints | No | Facts true of every endpoint, stated once (e.g. liveness is not observed). |
| total | Yes | Endpoints in the discovery cache before any filter. |
| fields | No | Detail level served: `compact` drops per-endpoint QoS detail, type id and notes. |
| by_topic | Yes | Roll-up over every matching endpoint. |
| returned | Yes | |
| domain_id | Yes | |
| endpoints | Yes | Matching endpoints, capped (see `truncated`). |
| truncated | Yes | True when matching endpoints exceeded the cap. |
| snapshot_ns | Yes | Wall-clock time of the snapshot, ns since epoch. |
| observer_guid | No | GUID of TopicForge's own participant, `None` in mock. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| departed_endpoint_count | No | Departed endpoints matching the filters; listed in `endpoints` only with `include_departed`. |
| hidden_internal_endpoint_count | No | Service/action endpoints (`rq/`, `rr/`, `rs/`, `rp/`, `ra/`, `ros_discovery_info`) left out while `include_internal` is false. |
| excluded_observer_endpoint_count | No | TopicForge's own endpoints left out of `endpoints` (counted in `total`). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (read-only, idempotent, non-destructive), yet the description adds substantial behavior beyond that: a 500-endpoint cap with `truncated`/`total`, a 200-entry/1-hour departed-endpoint cache, `busy` lock errors within a 45s bound, an MCP error with no active DDS module, mock-fixture behavior, and that `domain_id` is ignored and only range-checked. It even discloses semantic limits (cannot distinguish silent/hung writers, cannot observe instance ownership at runtime).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose and avoids fluff, but it is a long, bold-header-laden block; a few clauses (mock fixture, duration lock detail) could be tightened without loss. Every sentence remains informational rather than repetitive, so it stays readable for a discovery-heavy tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return shape need not be restated, and the description fills the remaining gaps: error modes, truncation, filtering interplay, departed-endpoint semantics, QoS `None` meaning, and orphan definitions in `by_topic`. Nothing an agent needs to call it correctly appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline would be 3; the description still adds value by explaining cross-parameter intent, e.g. that `compact` exists specifically for large buses and drops specific fields, that `topic` accepts either `rt/scan` or `scan`, and that `domain_id` does not switch domains. Most literal syntax is already in the schema, so it stops just short of a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ('List every DDS endpoint... announced on the bus') and enumerates the returned fields, so an agent knows exactly what it gets. It explicitly differentiates from sibling peek_dds_samples ('instead of parsing peek_dds_samples and joining GUID prefixes') and names detect_qos_mismatches as a complement rather than a substitute.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: use this instead of parsing peek_dds_samples, use `fields: compact` on large buses, scope with `topic`/`participant_guid` to read full QoS on one topic, and pair with detect_qos_mismatches because this tool cannot judge writer health. It also states what is excluded (observer, ROS 2 internal endpoints) and how to include them, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_nodesARead-onlyIdempotent
ROS 2 graph only; on a DDS-only setup use list_participants. List the nodes on the ROS 2 graph (or the mock graph) from one ros2 node list call as a NodeListing: name, namespace, full_name, duplicate_count, lifecycle (managed node; state not read, lifecycle_note if unknown), by_namespace counts. duplicates lists full names used by several nodes (a per-node tool then answers for one of them, unidentified). Nodes starting with an underscore are hidden and a node on another ROS_DOMAIN_ID is invisible. Empty nodes when the graph has none. Use get_node_info for one node's interfaces. Raises an MCP error without a ros2 CLI. Read-only; cheap (two CLI calls). Duration: ros2 CLI behind the ROS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| nodes | Yes | Nodes on the graph, one entry per distinct full name, sorted by name. |
| total | Yes | Distinct node names on the graph (no cap applies). |
| returned | Yes | Length of `nodes`. |
| truncated | Yes | True when `returned` is less than `total` because of a cap. |
| duplicates | Yes | Full names that appear more than once on the graph. Empty when every name is unique. |
| by_namespace | No | Distinct full names per namespace (`/` is the root), sorted by namespace. |
| lifecycle_note | No | Why `lifecycle` could not be determined (service list failed), else `None`. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| lifecycle_count | No | Nodes with `lifecycle` true. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations it discloses concrete operational traits: raises an MCP error without the `ros2` CLI, completes within 45 s, serializes behind the ROS lock and can return a `busy` error requiring retry, and returns an empty `nodes` list rather than failing on an empty graph. It also notes hidden underscore-prefixed nodes and nodes on other `ROS_DOMAIN_ID`s are invisible — a genuine discovery caveat.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the ROS 2 vs DDS scoping rule and error/duration notes bolded for scanning. The middle sentence enumerating every `NodeListing` field is dense and slightly over-stuffed given an output schema exists, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers call path, failure modes, timing, concurrency, visibility exclusions and empty-result semantics, and the return structure is backed by an output schema. An agent has everything needed to invoke and interpret this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, which is the baseline-4 case; there are no argument semantics to explain. The description instead spends its words on return shape and error behavior, which is the right allocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the nodes on the ROS 2 graph') and immediately scopes it against siblings: `list_participants` for DDS-only setups and `get_node_info` for a single node's interfaces. The agent can pick the right tool without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing rules are given in both directions: use this tool on the ROS 2 graph, use `list_participants` on a DDS-only setup, and use `get_node_info` to get one node's interfaces. Preconditions (ros2 CLI required) are also stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_participantsARead-onlyIdempotent
List DDS participants observed on the bus as a ParticipantListing: guid, vendor (read from the GUID prefix, unknown for vendors that do not put theirs there, e.g. Dust DDS and RTI Connext; see vendor_source), optional name (the announced EntityName, e.g. lidar_driver; a participant with none is normal, not a fault: identify it by its topics), hostname, domain_id and is_observer (TopicForge's own read-only participant, listed like any other). Lifecycle: status (active / left), first_seen_ns / last_seen_ns (TopicForge's clock), announced_ns (DDS source timestamp) and, once left, lost_ns + lost_time_source: an upper bound of the death (exact after a clean shutdown; after a crash the lease expiry, so up to one lease earlier: 10 s Cyclone, 20 s Fast DDS, 100 s RTI). Restarts: a restarted node is a new GUID with the same name; restarts_by_name gives lives (GUIDs) and departures per name, so do not count GUIDs by hand (seen_count is not a restart count). Cyclone tracks discovery in the background; right after start the call waits up to 3 s for it. Only the domain joined at startup is observed (health_check dds_domain_id): a participant on another domain is INVISIBLE here, and domain_id does not switch domains (restart with TOPICFORGE_DDS_DOMAIN_ID). Works at the raw DDS layer, so non-ROS participants appear too. Read-only by architecture: it cannot publish, modify QoS or alter the bus. Raises an MCP error when no DDS module is active (install topicforge[dds], set TOPICFORGE_DDS_BACKEND=cyclone); the mock backend returns fixtures. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| domain_id | No | Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| total | Yes | |
| returned | Yes | |
| domain_id | Yes | DDS domain observed (joined at startup). |
| truncated | Yes | |
| participants | Yes | |
| restarts_note | No | Set when unnamed participants were left out of `restarts_by_name`. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| restarts_by_name | No | Names seen with several GUIDs or lost at least once (absent: ran once and stayed). A restart is a new GUID with the same name. |
| restarts_by_node | No | `restarts_by_name` keyed by ROS 2 node full name. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite annotations already declaring read-only and idempotent behavior, the description adds substantial behavioral detail: startup discovery wait up to 3 s, visibility limited to the startup domain, mock backend behavior, MCP errors when no DDS module is active, lock contention with a busy error, and a 45 s duration bound. This is unusually complete transparency beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loaded and structured with bold sections, and almost every sentence conveys operational meaning. Some return-field detail may duplicate the output schema, but the field semantics it adds, such as unknown vendors and optional names, are useful rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex DDS discovery tool with an output schema and safety annotations, the description covers the important operational gaps: domain visibility, restart counting, lifecycle timestamps, error conditions, backend requirements, and duration limits. An agent has enough context to invoke and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is one parameter and schema description coverage is 100%, so the schema already explains that domain_id is only range-checked and does not switch domains. The description reinforces that constraint but does not add syntax or format details beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: list DDS participants observed on the bus, returned as a ParticipantListing. It also distinguishes the tool from ROS-graph tools by noting it works at the raw DDS layer, so non-ROS participants appear too.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is implied through domain restrictions and restart semantics, but the description never explicitly names alternatives such as participant_events or list_nodes and never says when to choose this tool over them. An agent can infer the use case, but the guidance is not explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_topicsARead-onlyIdempotent
ROS 2 graph only; on a DDS-only setup use list_endpoints. List the topics on the ROS 2 graph (or the mock graph) as a TopicListing: each has name, message_type, publisher_count and subscriber_count; mode_effective tells a real graph from fixtures. QoS is not listed: use get_topic_info. Empty topics when the graph has none or live discovery times out. Raises an MCP error without a ros2 CLI. Read-only; no side effects. Duration: ros2 CLI behind the ROS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| total | Yes | |
| topics | Yes | |
| returned | Yes | |
| truncated | Yes | |
| mode_effective | Yes | `live` or `mock` (fixtures). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/idempotent annotations: discloses empty-result semantics, MCP error behavior without the `ros2` CLI, a 45 s duration bound, and lock contention with a `busy` error requiring retry. That is exactly the operational context an agent needs before calling a discovery tool that can block on a lock.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the scope constraint and then uses bolded fragments for empty results, errors, and duration, so it scans well. Slightly dense and includes one redundant phrase ('Read-only; no side effects') already covered by annotations, but nearly every sentence carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even though an output schema exists, the description adds result-field meaning (`mode_effective`, publisher/subscriber counts) and the critical edge cases: empty topics, discovery timeout, missing CLI, and lock contention. Nothing an agent needs to decide whether to call it or interpret its failure modes is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With zero parameters the baseline is 4, and the description correctly avoids inventing parameter details. It instead explains the shape of what comes back, which is useful but belongs more to the output schema than to parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the topics on the ROS 2 graph') and immediately scopes it against the DDS-only alternative by naming `list_endpoints`. An agent can distinguish it from siblings like `get_topic_info` and `list_endpoints` without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes usage: ROS 2 graph only, use `list_endpoints` for DDS-only setups, and use `get_topic_info` because QoS is not included here. It also names the error condition (missing `ros2` CLI) and the retry case (busy lock), which is when-to-use guidance no sibling name alone would provide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
participant_eventsARead-onlyIdempotent
Return DDS participant lifecycle events (discovered / lost) from a recent window, e.g. 'who left in the last 5 minutes?', as a ParticipantEventListing, newest first: guid, event_type, vendor, timestamp_ns, time_source, observed_ns, age_s (seconds before the response; live only), optional name (announced name), hostname and domain_id. time_source is dds_source_timestamp (timestamp of the announcement or dispose) or observed_local (when TopicForge noticed, weakest). Crash caveat: a lost timestamp is an upper bound of the death: exact after a clean shutdown, the lease expiry after a crash, so the process died up to one lease earlier (10 s Cyclone, 20 s Fast DDS, 100 s RTI); the two cases cannot be told apart. Restarts: a restarted node is a new participant (one lost and one discovered, different guid, same name); restarts_by_name gives lives and departures per name over the window, so do not count GUIDs by hand. Capped at 200 events (truncated, note; reduce lookback_s). TopicForge only knows what happened since it started watching (health_check.observer_started_ns). Backends: Fast DDS uses listener callbacks; Cyclone polls discovery in the background every 0.5 s, so a participant cycle faster than the reader's history depth between two passes can be missed; mock returns a fixture timeline. Right after start the call waits up to 3 s for discovery. Read-only by architecture. Raises an MCP error when no DDS module is active. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| domain_id | No | Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value. | |
| lookback_s | No | Window in seconds, 1..86400, default 300. A larger window may hit the 200-event cap. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| total | Yes | |
| events | Yes | |
| returned | Yes | |
| domain_id | Yes | DDS domain observed (joined at startup). |
| truncated | Yes | True when cut to the 200-event cap. |
| restarts_note | No | Set when unnamed participants were left out of `restarts_by_name`. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| restarts_by_name | No | Names seen with several GUIDs or lost at least once (absent: ran once and stayed). A restart is a new GUID with the same name. Window only. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only/idempotent/non-destructive, yet the description adds far more: the crash caveat (lost timestamp is an upper bound, lease expiry of 10s/20s/100s), restart semantics, the 200-event cap with truncated/note, the observer-start limitation, backend-specific behavior (Cyclone's 0.5s poll can miss fast cycles), an MCP error when no DDS module, and lock/busy/duration behavior. This is behavior the agent cannot get from annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and output shape are front-loaded, and bold section labels (crash caveat, restarts, backends) make the dense block scannable. It is long, but nearly every clause carries distinct behavioral information for a genuinely complex tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still clarifies the non-obvious return semantics (time_source strengths, age_s being live-only, restarts_by_name), and covers the operational constraints an agent needs. Nothing required to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented, including the 'domain_id is ignored' caveat and lookback_s range. The description only restates the window-to-cap relationship ('reduce `lookback_s`'), which adds little beyond the schema's own note that a larger window may hit the cap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return DDS participant lifecycle events (`discovered` / `lost`)') with a concrete example query ('who left in the last 5 minutes?'). The event/lifecycle focus clearly distinguishes it from sibling list_participants, which reports current membership rather than a history of joins and leaves.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear usage scenario via the example query and actionable recovery steps ('reduce `lookback_s`' when truncated, 'retry' on `busy`). It never explicitly names a sibling (e.g. list_participants) as the alternative for the current-state case, so the when-not condition is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
peek_bag_samplesARead-onlyIdempotent
Peek up to count samples from a recorded bag file as a SampleResult: offline, for post-mortem (peek_dds_samples and sample_messages are live). Formats, by extension: MCAP (.mcap), rosbag2 SQLite (.db3), ROS 1 (.bag). Returns the first count messages of the topic in recording order, not the last (sample_messages keeps the newest log matches). start_s / end_s limit it to a bag-relative time range, empty with a note past the end; on /rosout, min_log_level and node keep the first count matching logs in range. Clocks: timestamp_ns is header.stamp when the message has a top-level header (stamp_source header), else the time in the body of Clock, TFMessage or Log (payload), else the bag record time (recorded); recorded_ns is always the record time. Each payload carries _decode_status (full / partial / raw). count defaults to 5, clamped to 50. Uses the rosbags library and needs the ROS 2 side of the runtime (an error on a DDS-only setup). Mock returns fixtures on canned paths. A bag without embedded definitions (Humble .db3) is decoded with those of its recorded distro, or Humble; note says which. Arrays over 4096 elements are cut and note lists them. Read-only by architecture. Raises an MCP error when the path does not exist, the topic is not in the bag, or rosbags is missing. Summary and rate: LaserScan, Odometry, Imu, Image and PointCloud2 samples carry a summary of the whole message (other types: null); rate gives frequency, interval spread and a verdict measured on recorded_ns (the bag record time of the returned messages). Use count >= 10 (50 above 20 Hz); a multi-publisher topic such as /tf reads erratic without being broken. Duration: takes no lock (in-process file read, so it runs while a ROS 2 or DDS call is busy); the time follows the bag's size and how deep the first samples are.
| Name | Required | Description | Default |
|---|---|---|---|
| node | No | `/rosout` only: keep logs of this node and its child loggers, e.g. `/robot1/driver` (also `driver`, `robot1.driver`). Exact name, never a prefix. Other topics: error. | |
| path | Yes | Bag path: a `.mcap` or `.db3` file or a `rosbag2_*` directory (`peek_bag_samples` also reads ROS 1 `.bag`; `analyze_bag` does not). Existence and format are checked live; mock accepts any well-formed path. | |
| count | No | Messages to return, default 5, at most 50 (`health_check.max_sample_count`): more is capped to 50, negative raises an error. The result's `count` may be lower. | |
| end_s | No | Only messages recorded before this bag-relative second (exclusive), default to the end. | |
| topic | Yes | Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI. | |
| start_s | No | Only messages recorded at or after this many seconds from the bag's first message (`analyze_bag.start_ns`), default from the start. | |
| min_log_level | No | `/rosout` (`rcl_interfaces/msg/Log`) only: keep messages at or above this level (debug 10, info 20, warn 30, error 40, fatal 50). Other topics: error. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Why `samples` is empty or limited, when not obvious; else `None`. |
| rate | No | How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10. |
| count | Yes | Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50). |
| topic | Yes | |
| samples | Yes | The samples, in backend order. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only/idempotent/closed-world, and the description adds substantial context beyond them: which error conditions raise an MCP error, that it runs without a lock while other calls are busy, that it needs the ROS 2 side of the runtime, decode-status semantics, clamping behavior, and rate/verdict caveats for multi-publisher topics. Nothing contradicts the annotations — 'read-only by architecture' and offline/mock behavior match readOnlyHint and openWorldHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and well-organized: purpose and null-hypothesis sibling contrast come first, then time range, clocks, errors, rate, and duration in ordered blocks. It is dense and slightly over-stuffed — the clock-resolution and duration paragraphs border on reference documentation — but almost every sentence carries invocation-relevant information, and repeated facts (count default/clamp) are a minor redundancy with the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter tool with an output schema, the description is more than complete: it explains result fields (`_decode_status`, `timestamp_ns` sources, `summary`, `rate`), failure modes, and environment prerequisites, so an agent can predict both success and error behavior. Nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description earns extra credit by explaining cross-parameter semantics the schema states only piecewise — that `min_log_level`/`node` apply only on `/rosout` and jointly keep the first `count` matching logs within the `start_s`/`end_s` window, and that `start_s` is measured from the bag's first message. Remaining parameters (path, topic) get no added meaning beyond schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific verb and resource (peek samples from a recorded bag file) and immediately scopes it as offline/post-mortem, explicitly contrasting with `peek_dds_samples` and `sample_messages` which are live. An agent can distinguish it from every sibling without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use this tool (offline post-mortem on recorded bags) versus the live alternatives by name, and notes the ordering contract (first `count` messages, not newest like `sample_messages`). It also flags the ROS 1 `.bag` support that `analyze_bag` lacks, giving an explicit selection criterion between the two bag readers.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
peek_dds_samplesARead-onlyIdempotent
Peek recent samples on a raw DDS topic as a SampleResult, like sample_messages but read from the DDS layer (works without ROS 2). count defaults to 5, clamped to 50. (a) The builtin discovery topics (DCPSParticipant, DCPSSubscription, DCPSPublication) return structured discovery records: on Cyclone the CURRENT state (one per live participant or endpoint), not recent events (use participant_events); DCPSPublication / DCPSSubscription are the raw data behind list_endpoints. The topic may be /scan, scan or rt/scan. (b) User topics: payload decoding is DISABLED on every backend, so the call returns count 0, empty samples and a note; that does NOT mean the topic is silent. Use list_endpoints for its presence, writers, readers and QoS. A user topic not announced on the bus raises an error. Right after start the call waits up to 3 s for discovery. Read-only by architecture: it cannot publish. Raises an MCP error when no DDS module is active or the topic is not announced. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | Messages to return, default 5, at most 50 (`health_check.max_sample_count`): more is capped to 50, negative raises an error. The result's `count` may be lower. | |
| topic | Yes | DDS topic name: bare (`scan`), ROS 2 mangled (`rt/scan`) or a builtin discovery topic (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Letters, digits, `_`, `/` and `::` only. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Why `samples` is empty or limited, when not obvious; else `None`. |
| rate | No | How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10. |
| count | Yes | Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50). |
| topic | Yes | |
| samples | Yes | The samples, in backend order. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only and open-world annotations, the description discloses important behavior: payload decoding is disabled for user topics, such calls return a note and empty samples rather than proving silence, discovery can wait up to 3 seconds, and lock contention produces a retryable busy error. It also explains that the tool cannot publish and raises MCP errors when no DDS module is active or the topic is not announced.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and sibling differentiation, then organized into builtin-topic and user-topic cases. It is dense and long, with some repetition of schema details, but most clauses carry operationally important edge cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, error modes, discovery timing, and lock behavior, the description covers what an agent needs to call it correctly. An output schema exists, so return-value structure does not need to be spelled out further.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents both parameters, including the default, cap, and accepted topic forms. The description reinforces this with examples like `/scan`, `scan`, and `rt/scan`, but adds little meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: peeking recent samples on a raw DDS topic, returning a SampleResult. It explicitly distinguishes itself from `sample_messages` by reading from the DDS layer and working without ROS 2.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit branching guidance: builtin discovery topics return current-state records and should use `participant_events` for recent events, while `list_endpoints` should be used for user-topic presence, writers, readers, and QoS. It also explains error conditions and retry behavior when the DDS lock is busy.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sample_messagesARead-onlyIdempotent
ROS 2 graph only; on a DDS-only setup use list_endpoints or peek_dds_samples on DCPSPublication / DCPSSubscription. Collect up to count live messages from a ROS 2 topic, waiting at most timeout_s seconds, as a SampleResult. Live mode streams ros2 topic echo with the publishers' QoS (latched topics work) and returns what arrived by the deadline: count is the actual number and note says N of M messages and why it is short (no publisher, silent, slow or too large to print in time; a latched topic usually holds one message, so use count 1). Each sample has payload (nested named values), timestamp_ns (the publisher's clock, sim time on a simulation, 0 for a headerless message such as std_msgs/String; see stamp_source) and received_ns (wall clock when the CLI printed it). Arrays: arrays, strings and bytes are cut at 128 elements by default (a 541-beam LaserScan loses beams 128 and up); cut fields are listed in payload._truncated_fields and note. Raise max_array_length (up to 65536, or null) or set arrays_summary_only. A message over the 1 MiB cap (TOPICFORGE_MAX_SAMPLE_BYTES) is dropped with a note. Mock mode returns demo-robot samples instantly. Raises an MCP error without a ros2 CLI, for an unknown topic, or when the CLI fails. Read-only; never publishes. Logs: min_log_level / node (/rosout only) stream until timeout_s (at most 2000 messages) and return the NEWEST count matches in stamp order; peek_bag_samples returns the FIRST matches. Summary and rate: LaserScan, Odometry, Imu, Image and PointCloud2 samples carry a summary of the whole message (other types: null); rate gives frequency, interval spread and a verdict measured on received_ns (when the CLI printed each message). Use count >= 10 (50 above 20 Hz); a multi-publisher topic such as /tf reads erratic without being broken. Duration: ros2 CLI behind the ROS lock, one call at a time; returns within timeout_s (at most 40) plus about 3 s to stop the CLI and decode; ends within 45 s, less time to collect after a wait; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| node | No | `/rosout` only: keep logs of this node and its child loggers, e.g. `/robot1/driver` (also `driver`, `robot1.driver`). Exact name, never a prefix. Other topics: error. | |
| count | No | Messages to wait for, default 5, at most 50 (more is capped and `note` says so); 0 returns nothing. Fewer arrive when the topic is slow or `timeout_s` runs out. | |
| topic | Yes | Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI. | |
| timeout_s | No | Seconds to wait, 1..40, default 10, covering topic lookup and CLI start-up. The call returns within `timeout_s` plus a few seconds, with what arrived and a `note`. Raise it for a topic slower than 1 Hz. | |
| min_log_level | No | `/rosout` (`rcl_interfaces/msg/Log`) only: keep messages at or above this level (debug 10, info 20, warn 30, error 40, fatal 50). Other topics: error. | |
| max_array_length | No | Longest array, string or bytes value returned in full, 1..65536, default 128 (a 541-beam `LaserScan` is cut to 128 ranges; its `summary` still covers all). Longer values are cut and listed in `_truncated_fields`. Null returns everything (large for images and clouds; a message over 1 MiB is dropped with a note). | |
| arrays_summary_only | No | When true, arrays become a type-and-length summary: use it to read the non-array fields of large messages. Default false. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Why `samples` is empty or limited, when not obvious; else `None`. |
| rate | No | How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10. |
| count | Yes | Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50). |
| topic | Yes | |
| samples | Yes | The samples, in backend order. |
| mode_effective | Yes | `live` or `mock` (fixtures). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only annotation, the description discloses QoS/latching behavior, timeout and truncation behavior, the 1 MiB cap, mock mode, MCP errors, lock contention and `busy` retry behavior, and total duration bounds. It never contradicts the annotations: it is consistently read-only and non-publishing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long and dense, but it is front-loaded with the graph/alternative caveat and then organized by mode, truncation, logging, summary/rate, and duration. Some parameter details duplicate the schema, which prevents a 5, but most sentences add useful operational context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex sampling tool with timeout, truncation, QoS, logging, and lock behavior, the description covers what an agent needs to invoke it correctly. An output schema exists, so it reasonably omits full return-value documentation while still explaining key result fields like `payload`, `note`, `summary`, and `rate`.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already explains each parameter, but the description adds operational meaning: `count` is the actual number returned, latched topics usually need `count` 1, and `node`/`min_log_level` apply only to `/rosout`. It also explains the practical effect of `max_array_length` and `arrays_summary_only` on large messages.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: collect up to `count` live messages from a ROS 2 `topic` as a `SampleResult`. It immediately distinguishes the ROS 2 graph scope from DDS-only workflows by naming `list_endpoints` and `peek_dds_samples` as alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says this is for the ROS 2 graph only and routes DDS-only setups to `list_endpoints` or `peek_dds_samples`. It also distinguishes live versus mock mode and explains the `peek_bag_samples` ordering difference for log-style queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
topic_metricsARead-onlyIdempotent
Return temporal metrics (frequency, sequence gaps, latency percentiles) for a DDS topic over a recent window as a TopicMetrics. Read status first: unsupported_user_topic means the payload is not decoded, so no metric exists: every null or 0 is not a measurement; no_samples_yet is a builtin topic with nothing buffered; ok means computed. Limits: the buffer is filled only when peek_dds_samples runs on the topic, so observed_frequency_hz reflects how often it was called, not the publish rate: a coarse presence signal. declared_frequency_hz is 1 / deadline of the shortest writer Deadline announced in discovery (declared, not measured; null when none). Also sequence_gaps_count, latency_ns_p50/p95/p99 and availability flags. Read-only by architecture. Raises an MCP error when no DDS module is active or window_s is outside 1..3600. Right after start the call waits up to 3 s for discovery. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | DDS topic name: bare (`scan`), ROS 2 mangled (`rt/scan`) or a builtin discovery topic (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Letters, digits, `_`, `/` and `::` only. | |
| window_s | No | Window in seconds, 1..3600, default 60. | |
| domain_id | No | Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | One sentence of context, or `None`. |
| topic | Yes | |
| status | No | `ok`; `no_samples_yet` (empty buffer); `unsupported_user_topic` (payload not decoded: null fields are not measurements). |
| window_s | Yes | Requested window in seconds (echoed). |
| latency_ns_p50 | No | Median publish-to-receive latency in ns; `None` unless `latency_available`. |
| latency_ns_p95 | No | 95th-percentile publish-to-receive latency (ns). |
| latency_ns_p99 | No | 99th-percentile publish-to-receive latency (ns). |
| mode_effective | Yes | `live` or `mock` (fixtures). |
| window_actual_s | Yes | Seconds actually covered; shorter than `window_s` after a recent start; `0.0` without samples. |
| samples_observed | Yes | Samples buffered in the window. `0` does NOT mean no publisher, only that no `peek_dds_samples` call captured one. |
| latency_available | No | True when a sample carried both a publish and a receive timestamp. |
| sequence_gaps_count | No | Missing sequence numbers in the buffered samples; `0` also when none exist (see `sequence_numbers_available`). |
| declared_frequency_hz | No | `1 / deadline` of the shortest writer Deadline announced: the promised rate, not the observed one; `None` when none announced. |
| observed_frequency_hz | No | `samples_observed / window_actual_s`; `None` under 2 samples. |
| sequence_numbers_available | No | True when a sequence number could be read from a sample. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only annotations, the description explains status values, what null/zero values mean, the distinction between observed and declared frequency, error conditions, discovery wait, locking, duration, and busy retry behavior. This is unusually rich behavioral context for an agent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well front-loaded: purpose first, then critical status semantics, then limits, errors, and concurrency. Despite its length, every section provides operationally useful information with no evident filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, existing output schema, and annotations, the description covers the critical gaps an agent needs: status interpretation, buffer limitations, domain behavior, errors, and timing. Nothing essential for correct invocation appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents topic, window_s, and domain_id semantics thoroughly. The description reinforces window bounds and domain_id behavior but adds little parameter meaning beyond the structured schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific operation and resource: return temporal metrics for a DDS topic, naming frequency, sequence gaps, and latency percentiles. It clearly distinguishes the tool as a metrics-computation endpoint rather than a raw sample reader like peek_dds_samples.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear operational guidance: read status first, and understand that the buffer is populated only when peek_dds_samples runs on the topic. It does not explicitly compare this tool against all sibling alternatives, so it stops short of a full when-to-use/when-not matrix.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
16 tool updates
v0.8.0- Changed
analyze_bag31 fields changed- removed
Output schema / $defs / BagTopicStats / properties / first_timestamp_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / BagTopicStats / properties / first_timestamp_ns / defaultRemoved value: -null - added
Output schema / $defs / BagTopicStats / properties / first_timestamp_ns / minimumAdded value: +0 - added
Output schema / $defs / BagTopicStats / properties / first_timestamp_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / BagTopicStats / properties / frequency_basis / anyOfRemoved value: -[ - { - "enum": [ - "topic_span", - "bag_duration" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / BagTopicStats / properties / frequency_basis / defaultRemoved value: -null - added
Output schema / $defs / BagTopicStats / properties / frequency_basis / enumAdded value: +[ + "topic_span", + "bag_duration", + null +] - added
Output schema / $defs / BagTopicStats / properties / frequency_basis / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / BagTopicStats / properties / frequency_hz / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "number" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / BagTopicStats / properties / frequency_hz / defaultRemoved value: -null - added
Output schema / $defs / BagTopicStats / properties / frequency_hz / minimumAdded value: +0 - added
Output schema / $defs / BagTopicStats / properties / frequency_hz / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / BagTopicStats / properties / kind / defaultRemoved value: -"user" - removed
Output schema / $defs / BagTopicStats / properties / last_timestamp_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / BagTopicStats / properties / last_timestamp_ns / defaultRemoved value: -null - added
Output schema / $defs / BagTopicStats / properties / last_timestamp_ns / minimumAdded value: +0 - added
Output schema / $defs / BagTopicStats / properties / last_timestamp_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / BagTopicStats / properties / latched / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / BagTopicStats / properties / latched / defaultRemoved value: -null - added
Output schema / $defs / BagTopicStats / properties / latched / typeAdded value: +[ + "boolean", + "null" +] - removed
Output schema / properties / bag_format / anyOfRemoved value: -[ - { - "enum": [ - "mcap", - "db3", - "bag", - "unknown" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / bag_format / defaultRemoved value: -null - added
Output schema / properties / bag_format / enumAdded value: +[ + "mcap", + "db3", + "bag", + "unknown", + null +] - added
Output schema / properties / bag_format / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / start_nsAdded value: +{ + "description": "Record time in ns of the bag's first message (rosbag2 `starting_time`): the zero of `peek_bag_samples` `start_s` / `end_s`. `null` when unreadable.", + "type": [ + "integer", + "null" + ] +} - removed
Output schema / properties / storage_format / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / storage_format / defaultRemoved value: -null - added
Output schema / properties / storage_format / typeAdded value: +[ + "string", + "null" +]
- Changed
detect_qos_mismatches53 fields changed- removed
Output schema / $defs / MatchedPair / properties / late_joiner / defaultRemoved value: -false - removed
Output schema / $defs / MatchedPair / properties / late_joiner_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MatchedPair / properties / late_joiner_note / defaultRemoved value: -null - added
Output schema / $defs / MatchedPair / properties / late_joiner_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MatchedPair / properties / reader_participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MatchedPair / properties / reader_participant_name / defaultRemoved value: -null - added
Output schema / $defs / MatchedPair / properties / reader_participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MatchedPair / properties / type_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MatchedPair / properties / type_name / defaultRemoved value: -null - added
Output schema / $defs / MatchedPair / properties / type_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MatchedPair / properties / writer_participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MatchedPair / properties / writer_participant_name / defaultRemoved value: -null - added
Output schema / $defs / MatchedPair / properties / writer_participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / reader_guid / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / reader_guid / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / reader_guid / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / reader_participant_guid / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / reader_participant_guid / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / reader_participant_guid / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / reader_participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / reader_participant_name / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / reader_participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / reader_type_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / reader_type_name / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / reader_type_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / writer_guid / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / writer_guid / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / writer_guid / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / writer_participant_guid / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / writer_participant_guid / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / writer_participant_guid / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / writer_participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / writer_participant_name / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / writer_participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / MismatchReport / properties / writer_type_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MismatchReport / properties / writer_type_name / defaultRemoved value: -null - added
Output schema / $defs / MismatchReport / properties / writer_type_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / NotMatchedPair / properties / reader_participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / NotMatchedPair / properties / reader_participant_name / defaultRemoved value: -null - added
Output schema / $defs / NotMatchedPair / properties / reader_participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / NotMatchedPair / properties / writer_participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / NotMatchedPair / properties / writer_participant_name / defaultRemoved value: -null - added
Output schema / $defs / NotMatchedPair / properties / writer_participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / matched_total / defaultRemoved value: -0 - removed
Output schema / properties / not_matched_total / defaultRemoved value: -0 - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / reports_total / defaultRemoved value: -0 - removed
Output schema / properties / summary / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / summary / defaultRemoved value: -null - added
Output schema / properties / summary / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / truncated / defaultRemoved value: -false
- Changed
get_node_info25 fields changed- added
Input schema / properties / params_fileAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Server path of a ROS 2 parameter `.yaml` (1 MiB max)." +} - removed
Output schema / $defs / NodeParameter / properties / original_size / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / NodeParameter / properties / original_size / defaultRemoved value: -null - added
Output schema / $defs / NodeParameter / properties / original_size / typeAdded value: +[ + "integer", + "null" +] - added
Output schema / $defs / ParamDiffAdded value: +{ + "additionalProperties": false, + "properties": { + "declared": { + "$ref": "#/$defs/JsonValue" + }, + "live": { + "$ref": "#/$defs/JsonValue" + }, + "name": { + "type": "string" + }, + "status": { + "description": "`declared_only`: absent on the node.", + "enum": [ + "different", + "declared_only", + "masked" + ], + "type": "string" + } + }, + "required": [ + "name", + "status", + "declared", + "live" + ], + "type": "object" +} - added
Output schema / properties / lifecycleAdded value: +{ + "description": "True when `service_servers` has `<full_name>/get_state` (GetState).", + "type": "boolean" +} - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / param_diffAdded value: +{ + "description": "File against node: differences only.", + "items": { + "$ref": "#/$defs/ParamDiff" + }, + "type": [ + "array", + "null" + ] +} - added
Output schema / properties / param_diff_noteAdded value: +{ + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / param_diff_summaryAdded value: +{ + "description": "Counts left out: same, live_only.", + "type": [ + "string", + "null" + ] +} - removed
Output schema / properties / parameters / anyOfRemoved value: -[ - { - "items": { - "$ref": "#/$defs/NodeParameter" - }, - "type": "array" - }, - { - "type": "null" - } -] - removed
Output schema / properties / parameters / defaultRemoved value: -null - added
Output schema / properties / parameters / itemsAdded value: +{ + "$ref": "#/$defs/NodeParameter" +} - added
Output schema / properties / parameters / typeAdded value: +[ + "array", + "null" +] - removed
Output schema / properties / parameters_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / parameters_note / defaultRemoved value: -null - added
Output schema / properties / parameters_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / use_sim_time / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - removed
Output schema / properties / use_sim_time / defaultRemoved value: -null - added
Output schema / properties / use_sim_time / typeAdded value: +[ + "boolean", + "null" +] - removed
Output schema / properties / use_sim_time_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / use_sim_time_note / defaultRemoved value: -null - added
Output schema / properties / use_sim_time_note / typeAdded value: +[ + "string", + "null" +]
- Changed
get_topic_info11 fields changed- removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / publisher_qos / defaultRemoved value: -null - removed
Output schema / properties / publisher_qos_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / publisher_qos_note / defaultRemoved value: -null - added
Output schema / properties / publisher_qos_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / subscription_qos / defaultRemoved value: -null - removed
Output schema / properties / subscription_qos_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / subscription_qos_note / defaultRemoved value: -null - added
Output schema / properties / subscription_qos_note / typeAdded value: +[ + "string", + "null" +]
- Added
graph_diff - Changed
health_check70 fields changed- removed
Output schema / properties / contract_version / defaultRemoved value: -2 - changed
Output schema / properties / contract_version / descriptionPrevious value: -"Output contract version; changes only when a field is renamed, removed or retyped."New value: +"Output contract version; changes when a field is renamed, removed or retyped." - removed
Output schema / properties / dds_backend / defaultRemoved value: -"none" - removed
Output schema / properties / dds_domain_id / anyOfRemoved value: -[ - { - "maximum": 232, - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / dds_domain_id / defaultRemoved value: -null - added
Output schema / properties / dds_domain_id / maximumAdded value: +232 - added
Output schema / properties / dds_domain_id / minimumAdded value: +0 - added
Output schema / properties / dds_domain_id / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / dds_inactive_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / dds_inactive_note / defaultRemoved value: -null - changed
Output schema / properties / dds_inactive_note / descriptionPrevious value: -"Why `dds_backend` is `none` while ROS 2 serves (not selected, binding missing, or failed to start); else `null`."New value: +"Why `dds_backend` is `none` while ROS 2 serves; else `null`." - added
Output schema / properties / dds_inactive_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / dds_security / defaultRemoved value: -"not_supported" - removed
Output schema / properties / middleware_available / defaultRemoved value: -false - changed
Output schema / properties / mode / descriptionPrevious value: -"Mode actually serving: `mock` or `live` (`live` with `ros_backend` `none`: DDS tools only). May differ from `requested_mode` after a fallback."New value: +"Mode serving: `mock` or `live` (`live` with `ros_backend` `none`: DDS tools only); may differ from `requested_mode` after a fallback." - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / now_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / now_ns / defaultRemoved value: -null - added
Output schema / properties / now_ns / minimumAdded value: +0 - added
Output schema / properties / now_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / observed_domain_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / observed_domain_note / defaultRemoved value: -null - added
Output schema / properties / observed_domain_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / observer_started_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / observer_started_ns / defaultRemoved value: -null - added
Output schema / properties / observer_started_ns / minimumAdded value: +0 - added
Output schema / properties / observer_started_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / payload_decoding / defaultRemoved value: -"disabled" - removed
Output schema / properties / payload_decoding_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / payload_decoding_note / defaultRemoved value: -"user-topic payload decoding is switched off until it is validated on a real bus; builtin discovery topics are still readable" - added
Output schema / properties / payload_decoding_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / rmw_implementation / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / rmw_implementation / defaultRemoved value: -null - added
Output schema / properties / rmw_implementation / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / rmw_source / defaultRemoved value: -"none" - removed
Output schema / properties / ros2_distro / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / ros2_distro / defaultRemoved value: -null - added
Output schema / properties / ros2_distro / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / ros_backend / defaultRemoved value: -"none" - added
Output schema / properties / ros_security_enabledAdded value: +{ + "description": "`ROS_SECURITY_ENABLE` is `true`; `None` unless the ros2 CLI serves.", + "type": [ + "boolean", + "null" + ] +} - added
Output schema / properties / ros_security_keystore_setAdded value: +{ + "description": "Whether a keystore variable is set (the path is never shown).", + "type": [ + "boolean", + "null" + ] +} - added
Output schema / properties / ros_security_noteAdded value: +{ + "description": "Set when `ros_security_enabled`: what stays invisible.", + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / ros_security_strategyAdded value: +{ + "description": "`ROS_SECURITY_STRATEGY` when set to a valid value.", + "enum": [ + "Enforce", + "Permissive", + null + ], + "type": [ + "string", + "null" + ] +} - removed
Output schema / properties / ros_tools_available / defaultRemoved value: -false - removed
Output schema / properties / sim_clock_published / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - removed
Output schema / properties / sim_clock_published / defaultRemoved value: -null - changed
Output schema / properties / sim_clock_published / descriptionPrevious value: -"Whether `/clock` had a publisher in the last graph read (a hint that stamps may be sim time). `None` in mock or without a recent read; `health_check` never runs `ros2`."New value: +"Whether `/clock` had a publisher at the last graph read (stamps may be sim time); `None` in mock or without a recent read." - added
Output schema / properties / sim_clock_published / typeAdded value: +[ + "boolean", + "null" +] - removed
Output schema / properties / tracker_cache_evictions / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / tracker_cache_evictions / defaultRemoved value: -null - changed
Output schema / properties / tracker_cache_evictions / descriptionPrevious value: -"Discovery entries dropped from a full tracker cache (4096 each); non-zero means the bus is bigger than listed."New value: +"Discovery entries dropped from a full tracker cache; non-zero means the bus is bigger than listed." - added
Output schema / properties / tracker_cache_evictions / minimumAdded value: +0 - added
Output schema / properties / tracker_cache_evictions / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / tracker_errors / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / tracker_errors / defaultRemoved value: -null - added
Output schema / properties / tracker_errors / minimumAdded value: +0 - added
Output schema / properties / tracker_errors / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / tracker_last_pass_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / tracker_last_pass_ns / defaultRemoved value: -null - added
Output schema / properties / tracker_last_pass_ns / minimumAdded value: +0 - added
Output schema / properties / tracker_last_pass_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / tracker_passes / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / tracker_passes / defaultRemoved value: -null - added
Output schema / properties / tracker_passes / minimumAdded value: +0 - added
Output schema / properties / tracker_passes / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / tracker_running / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - removed
Output schema / properties / tracker_running / defaultRemoved value: -null - added
Output schema / properties / tracker_running / typeAdded value: +[ + "boolean", + "null" +]
- Added
inspect_tf - Changed
list_endpoints103 fields changed- removed
Output schema / $defs / DepartedEndpoint / properties / gone_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / DepartedEndpoint / properties / gone_ns / defaultRemoved value: -null - added
Output schema / $defs / DepartedEndpoint / properties / gone_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / DepartedEndpoint / properties / participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / DepartedEndpoint / properties / participant_name / defaultRemoved value: -null - added
Output schema / $defs / DepartedEndpoint / properties / participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / announced_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / announced_ns / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / announced_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / gone_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / gone_ns / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / gone_ns / typeAdded value: +[ + "integer", + "null" +] - added
Output schema / $defs / EndpointInfo / properties / node_nameAdded value: +{ + "description": "ROS 2 node owning the endpoint (`ros_discovery_info`); `None` if not announced.", + "type": [ + "string", + "null" + ] +} - removed
Output schema / $defs / EndpointInfo / properties / participant_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / participant_name / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / participant_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / participant_vendor / defaultRemoved value: -"unknown" - removed
Output schema / $defs / EndpointInfo / properties / qos / defaultRemoved value: -null - removed
Output schema / $defs / EndpointInfo / properties / qos_summary / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / qos_summary / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / qos_summary / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / ros_topic / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / ros_topic / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / ros_topic / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / ros_topic_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / ros_topic_note / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / ros_topic_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / type_id / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / type_id / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / type_id / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / EndpointInfo / properties / type_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / EndpointInfo / properties / type_name / defaultRemoved value: -null - added
Output schema / $defs / EndpointInfo / properties / type_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / QosProfile / properties / data_representation / anyOfRemoved value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / data_representation / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / data_representation / itemsAdded value: +{ + "type": "string" +} - added
Output schema / $defs / QosProfile / properties / data_representation / typeAdded value: +[ + "array", + "null" +] - removed
Output schema / $defs / QosProfile / properties / deadline_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / deadline_ns / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / deadline_ns / minimumAdded value: +0 - added
Output schema / $defs / QosProfile / properties / deadline_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / QosProfile / properties / destination_order / anyOfRemoved value: -[ - { - "enum": [ - "BY_RECEPTION_TIMESTAMP", - "BY_SOURCE_TIMESTAMP" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / destination_order / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / destination_order / enumAdded value: +[ + "BY_RECEPTION_TIMESTAMP", + "BY_SOURCE_TIMESTAMP", + null +] - added
Output schema / $defs / QosProfile / properties / destination_order / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / QosProfile / properties / history / anyOfRemoved value: -[ - { - "enum": [ - "KEEP_LAST", - "KEEP_ALL" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / history / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / history / enumAdded value: +[ + "KEEP_LAST", + "KEEP_ALL", + null +] - added
Output schema / $defs / QosProfile / properties / history / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / QosProfile / properties / history_depth / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / history_depth / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / history_depth / minimumAdded value: +0 - added
Output schema / $defs / QosProfile / properties / history_depth / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / QosProfile / properties / history_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / history_note / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / history_note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / QosProfile / properties / latency_budget_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / latency_budget_ns / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / latency_budget_ns / minimumAdded value: +0 - added
Output schema / $defs / QosProfile / properties / latency_budget_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / QosProfile / properties / liveliness_kind / anyOfRemoved value: -[ - { - "enum": [ - "AUTOMATIC", - "MANUAL_BY_PARTICIPANT", - "MANUAL_BY_TOPIC" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / liveliness_kind / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / liveliness_kind / enumAdded value: +[ + "AUTOMATIC", + "MANUAL_BY_PARTICIPANT", + "MANUAL_BY_TOPIC", + null +] - added
Output schema / $defs / QosProfile / properties / liveliness_kind / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / QosProfile / properties / liveliness_lease_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / liveliness_lease_ns / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / liveliness_lease_ns / minimumAdded value: +0 - added
Output schema / $defs / QosProfile / properties / liveliness_lease_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / QosProfile / properties / ownership_kind / anyOfRemoved value: -[ - { - "enum": [ - "SHARED", - "EXCLUSIVE" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / ownership_kind / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / ownership_kind / enumAdded value: +[ + "SHARED", + "EXCLUSIVE", + null +] - added
Output schema / $defs / QosProfile / properties / ownership_kind / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / QosProfile / properties / ownership_strength / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / ownership_strength / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / ownership_strength / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / QosProfile / properties / partitions / anyOfRemoved value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / partitions / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / partitions / itemsAdded value: +{ + "type": "string" +} - added
Output schema / $defs / QosProfile / properties / partitions / typeAdded value: +[ + "array", + "null" +] - removed
Output schema / $defs / QosProfile / properties / unknown_policies / anyOfRemoved value: -[ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / QosProfile / properties / unknown_policies / defaultRemoved value: -null - added
Output schema / $defs / QosProfile / properties / unknown_policies / itemsAdded value: +{ + "type": "string" +} - added
Output schema / $defs / QosProfile / properties / unknown_policies / typeAdded value: +[ + "array", + "null" +] - removed
Output schema / $defs / TopicSummary / properties / orphan / anyOfRemoved value: -[ - { - "enum": [ - "no_reader", - "no_writer" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / TopicSummary / properties / orphan / defaultRemoved value: -null - added
Output schema / $defs / TopicSummary / properties / orphan / enumAdded value: +[ + "no_reader", + "no_writer", + null +] - added
Output schema / $defs / TopicSummary / properties / orphan / typeAdded value: +[ + "string", + "null" +] - added
Output schema / $defs / TopicSummary / properties / reader_nodesAdded value: +{ + "description": "Sorted ROS 2 nodes of the readers (`node_name`).", + "items": { + "type": "string" + }, + "type": "array" +} - removed
Output schema / $defs / TopicSummary / properties / ros_topic / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / TopicSummary / properties / ros_topic / defaultRemoved value: -null - added
Output schema / $defs / TopicSummary / properties / ros_topic / typeAdded value: +[ + "string", + "null" +] - added
Output schema / $defs / TopicSummary / properties / writer_nodesAdded value: +{ + "description": "Sorted ROS 2 nodes of the writers (`node_name`).", + "items": { + "type": "string" + }, + "type": "array" +} - removed
Output schema / descriptionRemoved value: -"Envelope returned by `list_endpoints`." - removed
Output schema / properties / departed_endpoint_count / defaultRemoved value: -0 - removed
Output schema / properties / excluded_observer_endpoint_count / defaultRemoved value: -0 - removed
Output schema / properties / fields / defaultRemoved value: -"full" - removed
Output schema / properties / hidden_internal_endpoint_count / defaultRemoved value: -0 - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / observer_guid / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / observer_guid / defaultRemoved value: -null - added
Output schema / properties / observer_guid / typeAdded value: +[ + "string", + "null" +]
- Changed
list_nodes8 fields changed- added
Output schema / $defs / NodeListItem / properties / lifecycleAdded value: +{ + "description": "True when the graph has `<full_name>/get_state` of type `lifecycle_msgs/srv/GetState` (a managed node). The state itself is never read.", + "type": "boolean" +} - removed
Output schema / descriptionRemoved value: -"Envelope returned by `list_nodes`." - added
Output schema / properties / by_namespaceAdded value: +{ + "additionalProperties": { + "type": "integer" + }, + "description": "Distinct full names per namespace (`/` is the root), sorted by namespace.", + "type": "object" +} - added
Output schema / properties / lifecycle_countAdded value: +{ + "description": "Nodes with `lifecycle` true.", + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / lifecycle_noteAdded value: +{ + "description": "Why `lifecycle` could not be determined (service list failed), else `None`.", + "type": [ + "string", + "null" + ] +} - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +]
- Changed
list_participants41 fields changed- removed
Output schema / $defs / ParticipantInfo / properties / announced_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / announced_ns / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / announced_ns / minimumAdded value: +0 - added
Output schema / $defs / ParticipantInfo / properties / announced_ns / typeAdded value: +[ + "integer", + "null" +] - added
Output schema / $defs / ParticipantInfo / properties / endpoint_countAdded value: +{ + "description": "Writers plus readers seen for this participant; `None` without an endpoint cache.", + "minimum": 0, + "type": [ + "integer", + "null" + ] +} - removed
Output schema / $defs / ParticipantInfo / properties / first_seen_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / first_seen_ns / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / first_seen_ns / minimumAdded value: +0 - added
Output schema / $defs / ParticipantInfo / properties / first_seen_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / ParticipantInfo / properties / hostname / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / hostname / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / hostname / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / ParticipantInfo / properties / is_observer / defaultRemoved value: -false - added
Output schema / $defs / ParticipantInfo / properties / is_topicforge_cliAdded value: +{ + "description": "True for a `ros2` CLI process TopicForge spawned (lingers ~2 s); not the robot.", + "type": "boolean" +} - removed
Output schema / $defs / ParticipantInfo / properties / last_seen_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / last_seen_ns / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / last_seen_ns / minimumAdded value: +0 - added
Output schema / $defs / ParticipantInfo / properties / last_seen_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / ParticipantInfo / properties / lost_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / lost_ns / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / lost_ns / minimumAdded value: +0 - added
Output schema / $defs / ParticipantInfo / properties / lost_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / ParticipantInfo / properties / lost_time_source / anyOfRemoved value: -[ - { - "enum": [ - "dds_source_timestamp", - "observed_local" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / lost_time_source / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / lost_time_source / enumAdded value: +[ + "dds_source_timestamp", + "observed_local", + null +] - added
Output schema / $defs / ParticipantInfo / properties / lost_time_source / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / ParticipantInfo / properties / name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantInfo / properties / name / defaultRemoved value: -null - added
Output schema / $defs / ParticipantInfo / properties / name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / ParticipantInfo / properties / node_names_source / defaultRemoved value: -"none" - removed
Output schema / $defs / ParticipantInfo / properties / seen_count / defaultRemoved value: -1 - removed
Output schema / $defs / ParticipantInfo / properties / status / defaultRemoved value: -"unknown" - removed
Output schema / $defs / ParticipantInfo / properties / vendor_source / defaultRemoved value: -"none" - removed
Output schema / descriptionRemoved value: -"Envelope returned by `list_participants`." - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / restarts_by_nodeAdded value: +{ + "additionalProperties": { + "$ref": "#/$defs/NameLifecycle" + }, + "description": "`restarts_by_name` keyed by ROS 2 node full name.", + "type": "object" +} - removed
Output schema / properties / restarts_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / restarts_note / defaultRemoved value: -null - added
Output schema / properties / restarts_note / typeAdded value: +[ + "string", + "null" +]
- Changed
list_topics4 fields changed- removed
Output schema / descriptionRemoved value: -"Envelope returned by `list_topics`." - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +]
- Changed
participant_events22 fields changed- removed
Output schema / $defs / ParticipantEvent / properties / age_s / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "number" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantEvent / properties / age_s / defaultRemoved value: -null - added
Output schema / $defs / ParticipantEvent / properties / age_s / minimumAdded value: +0 - added
Output schema / $defs / ParticipantEvent / properties / age_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / ParticipantEvent / properties / hostname / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantEvent / properties / hostname / defaultRemoved value: -null - added
Output schema / $defs / ParticipantEvent / properties / hostname / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / ParticipantEvent / properties / name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantEvent / properties / name / defaultRemoved value: -null - added
Output schema / $defs / ParticipantEvent / properties / name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / $defs / ParticipantEvent / properties / observed_ns / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / ParticipantEvent / properties / observed_ns / defaultRemoved value: -null - added
Output schema / $defs / ParticipantEvent / properties / observed_ns / minimumAdded value: +0 - added
Output schema / $defs / ParticipantEvent / properties / observed_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / ParticipantEvent / properties / time_source / defaultRemoved value: -"observed_local" - removed
Output schema / descriptionRemoved value: -"Envelope returned by `participant_events`." - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / restarts_note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / restarts_note / defaultRemoved value: -null - added
Output schema / properties / restarts_note / typeAdded value: +[ + "string", + "null" +]
- Changed
peek_bag_samples46 fields changed- added
Input schema / properties / end_sAdded value: +{ + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only messages recorded before this bag-relative second (exclusive), default to the end." +} - added
Input schema / properties / min_log_levelAdded value: +{ + "anyOf": [ + { + "enum": [ + "debug", + "info", + "warn", + "error", + "fatal" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "`/rosout` (`rcl_interfaces/msg/Log`) only: keep messages at or above this level (debug 10, info 20, warn 30, error 40, fatal 50). Other topics: error." +} - added
Input schema / properties / nodeAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "`/rosout` only: keep logs of this node and its child loggers, e.g. `/robot1/driver` (also `driver`, `robot1.driver`). Exact name, never a prefix. Other topics: error." +} - added
Input schema / properties / start_sAdded value: +{ + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only messages recorded at or after this many seconds from the bag's first message (`analyze_bag.start_ns`), default from the start." +} - removed
Output schema / $defs / ImageSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "data_length": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixel buffer size in bytes (see `data_length_basis`)." - }, - "data_length_basis": { - "anyOf": [ - { - "enum": [ - "payload", - "step_x_height" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)." - }, - "encoding": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Pixel encoding, e.g. `rgb8`." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the camera." - }, - "height": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixels." - }, - "is_bigendian": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "Big-endian pixels." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when partial; else `null`." - }, - "step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Row length, bytes." - }, - "summary_type": { - "const": "image", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixels." - } - }, - "required": [ - "summary_type", - "frame_id", - "width", - "height", - "encoding", - "step", - "is_bigendian", - "data_length", - "data_length_basis" - ], - "type": "object" -} - removed
Output schema / $defs / ImuSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angular_velocity_norm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `angular_velocity`, rad/s." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the IMU." - }, - "linear_acceleration_norm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `linear_acceleration`, m/s^2." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when a value is unusable; else `null`." - }, - "orientation_valid": { - "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.", - "type": "boolean" - }, - "pitch": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about y, radians." - }, - "roll": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about x, radians." - }, - "summary_type": { - "const": "imu", - "type": "string" - }, - "yaw": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about z, radians." - } - }, - "required": [ - "summary_type", - "frame_id", - "orientation_valid", - "roll", - "pitch", - "yaw", - "angular_velocity_norm", - "linear_acceleration_norm" - ], - "type": "object" -} - removed
Output schema / $defs / LaserScanSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angle_increment": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Angle between beams, rad." - }, - "angle_max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Bearing of the last beam, rad." - }, - "angle_min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Bearing of beam 0, rad." - }, - "beam_count": { - "description": "Beams in `ranges`.", - "minimum": 0, - "type": "integer" - }, - "closest_obstacle": { - "anyOf": [ - { - "$ref": "#/$defs/ScanReturn" - }, - { - "type": "null" - } - ], - "description": "Closest valid return of the scan; `null` with `note`." - }, - "finite_count": { - "description": "Beams with a finite range.", - "minimum": 0, - "type": "integer" - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame." - }, - "inf_count": { - "description": "Beams at `+inf`.", - "minimum": 0, - "type": "integer" - }, - "nan_count": { - "description": "Beams at `nan`.", - "minimum": 0, - "type": "integer" - }, - "neg_inf_count": { - "description": "Beams at `-inf`.", - "minimum": 0, - "type": "integer" - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when the summary is partial; else `null`." - }, - "out_of_range_count": { - "description": "Finite ranges outside `range_min`..`range_max` (not valid).", - "minimum": 0, - "type": "integer" - }, - "range_max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Largest valid range, m." - }, - "range_min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Smallest valid range, m." - }, - "sectors": { - "$ref": "#/$defs/ScanSectors", - "description": "Closest valid return per sector." - }, - "summary_type": { - "const": "laser_scan", - "type": "string" - } - }, - "required": [ - "summary_type", - "frame_id", - "beam_count", - "angle_min", - "angle_max", - "angle_increment", - "range_min", - "range_max", - "finite_count", - "inf_count", - "neg_inf_count", - "nan_count", - "out_of_range_count", - "closest_obstacle", - "sectors" - ], - "type": "object" -} - removed
Output schema / $defs / MessageSample / properties / received_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / received_ns / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / received_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / MessageSample / properties / recorded_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / recorded_ns / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / recorded_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / MessageSample / properties / summary / anyOfRemoved value: -[ - { - "discriminator": { - "mapping": { - "image": "#/$defs/ImageSummary", - "imu": "#/$defs/ImuSummary", - "laser_scan": "#/$defs/LaserScanSummary", - "odometry": "#/$defs/OdometrySummary", - "point_cloud2": "#/$defs/PointCloud2Summary" - }, - "propertyName": "summary_type" - }, - "oneOf": [ - { - "$ref": "#/$defs/LaserScanSummary" - }, - { - "$ref": "#/$defs/OdometrySummary" - }, - { - "$ref": "#/$defs/ImuSummary" - }, - { - "$ref": "#/$defs/ImageSummary" - }, - { - "$ref": "#/$defs/PointCloud2Summary" - } - ] - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / summary / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / summary / propertiesAdded value: +{ + "summary_type": { + "enum": [ + "laser_scan", + "odometry", + "imu", + "image", + "point_cloud2" + ], + "type": "string" + } +} - added
Output schema / $defs / MessageSample / properties / summary / requiredAdded value: +[ + "summary_type" +] - added
Output schema / $defs / MessageSample / properties / summary / typeAdded value: +[ + "object", + "null" +] - removed
Output schema / $defs / OdometrySummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angular_z": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`twist.twist.angular.z`, rad/s." - }, - "child_frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`child_frame_id` (frame of the twist)." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` (frame of the pose)." - }, - "linear_speed": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `twist.twist.linear`, m/s." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when a value is missing; else `null`." - }, - "position_x": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.x`, m." - }, - "position_y": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.y`, m." - }, - "position_z": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.z`, m." - }, - "summary_type": { - "const": "odometry", - "type": "string" - }, - "yaw": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Heading from the pose quaternion, radians." - } - }, - "required": [ - "summary_type", - "frame_id", - "child_frame_id", - "linear_speed", - "angular_z", - "position_x", - "position_y", - "position_z", - "yaw" - ], - "type": "object" -} - removed
Output schema / $defs / PointCloud2SummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "fields": { - "description": "Point layout.", - "items": { - "$ref": "#/$defs/PointFieldSummary" - }, - "type": "array" - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the cloud." - }, - "height": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Rows (1: unorganized)." - }, - "is_dense": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "True when no point is invalid." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when partial; else `null`." - }, - "point_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "`width` times `height`." - }, - "point_step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Bytes per point." - }, - "row_step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Bytes per row." - }, - "summary_type": { - "const": "point_cloud2", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Points per row." - } - }, - "required": [ - "summary_type", - "frame_id", - "width", - "height", - "point_count", - "point_step", - "row_step", - "is_dense", - "fields" - ], - "type": "object" -} - removed
Output schema / $defs / PointFieldSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Elements per point." - }, - "datatype": { - "description": "Element type, e.g. `FLOAT32`.", - "type": "string" - }, - "name": { - "description": "Field name, e.g. `x`.", - "type": "string" - }, - "offset": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Byte offset in a point." - } - }, - "required": [ - "name", - "datatype", - "offset", - "count" - ], - "type": "object" -} - removed
Output schema / $defs / ScanReturnRemoved value: -{ - "additionalProperties": false, - "properties": { - "beam_index": { - "description": "Index in `ranges`.", - "minimum": 0, - "type": "integer" - }, - "bearing": { - "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.", - "type": "number" - }, - "range": { - "description": "Distance, meters.", - "type": "number" - } - }, - "required": [ - "range", - "bearing", - "beam_index" - ], - "type": "object" -} - removed
Output schema / $defs / ScanSectorRemoved value: -{ - "additionalProperties": false, - "properties": { - "beam_count": { - "description": "Beams inside the sector (0: not covered).", - "minimum": 0, - "type": "integer" - }, - "closest": { - "anyOf": [ - { - "$ref": "#/$defs/ScanReturn" - }, - { - "type": "null" - } - ], - "description": "Closest valid return in the sector; `null` with `note`." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Why `closest` is `null`; `null` when it is set." - } - }, - "required": [ - "beam_count", - "closest" - ], - "type": "object" -} - removed
Output schema / $defs / ScanSectorsRemoved value: -{ - "additionalProperties": false, - "properties": { - "front": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings within +-45 degrees of +x." - }, - "left": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings from 45 to 135 degrees." - }, - "rear": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings beyond +-135 degrees." - }, - "right": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings from -135 to -45 degrees." - } - }, - "required": [ - "front", - "left", - "right", - "rear" - ], - "type": "object" -} - added
Output schema / $defs / TopicRate / properties / clock_basisAdded value: +{ + "description": "Stamp clock: `wall` (stamps are dates), `sim` (since a start), `none` (no stamps), `mixed` (several kinds, e.g. two publishers).", + "enum": [ + "wall", + "sim", + "none", + "mixed" + ], + "type": "string" +} - removed
Output schema / $defs / TopicRate / properties / interval_cv / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / interval_cv / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / interval_median_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / interval_median_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / max_gap_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / max_gap_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / mean_interval_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / mean_interval_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / observed_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / observed_frequency_hz / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / sim_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / sim_frequency_hz / typeAdded value: +[ + "number", + "null" +] - added
Output schema / $defs / TopicRate / properties / stamp_age_median_sAdded value: +{ + "description": "Median of arrival minus stamp, s, if `wall`; negative = publisher ahead.", + "type": [ + "number", + "null" + ] +} - added
Output schema / $defs / TopicRate / properties / stamp_age_p95_sAdded value: +{ + "description": "95th percentile of that age (max under 20 messages).", + "type": [ + "number", + "null" + ] +} - added
Output schema / $defs / TopicRate / properties / stamp_regression_countAdded value: +{ + "description": "Times a stamp went back versus the previous one.", + "minimum": 0, + "type": "integer" +} - removed
Output schema / $defs / TopicRate / properties / trailing_gap_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / trailing_gap_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / rate / defaultRemoved value: -null
- Changed
peek_dds_samples42 fields changed- removed
Output schema / $defs / ImageSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "data_length": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixel buffer size in bytes (see `data_length_basis`)." - }, - "data_length_basis": { - "anyOf": [ - { - "enum": [ - "payload", - "step_x_height" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)." - }, - "encoding": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Pixel encoding, e.g. `rgb8`." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the camera." - }, - "height": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixels." - }, - "is_bigendian": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "Big-endian pixels." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when partial; else `null`." - }, - "step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Row length, bytes." - }, - "summary_type": { - "const": "image", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixels." - } - }, - "required": [ - "summary_type", - "frame_id", - "width", - "height", - "encoding", - "step", - "is_bigendian", - "data_length", - "data_length_basis" - ], - "type": "object" -} - removed
Output schema / $defs / ImuSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angular_velocity_norm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `angular_velocity`, rad/s." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the IMU." - }, - "linear_acceleration_norm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `linear_acceleration`, m/s^2." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when a value is unusable; else `null`." - }, - "orientation_valid": { - "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.", - "type": "boolean" - }, - "pitch": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about y, radians." - }, - "roll": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about x, radians." - }, - "summary_type": { - "const": "imu", - "type": "string" - }, - "yaw": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about z, radians." - } - }, - "required": [ - "summary_type", - "frame_id", - "orientation_valid", - "roll", - "pitch", - "yaw", - "angular_velocity_norm", - "linear_acceleration_norm" - ], - "type": "object" -} - removed
Output schema / $defs / LaserScanSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angle_increment": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Angle between beams, rad." - }, - "angle_max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Bearing of the last beam, rad." - }, - "angle_min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Bearing of beam 0, rad." - }, - "beam_count": { - "description": "Beams in `ranges`.", - "minimum": 0, - "type": "integer" - }, - "closest_obstacle": { - "anyOf": [ - { - "$ref": "#/$defs/ScanReturn" - }, - { - "type": "null" - } - ], - "description": "Closest valid return of the scan; `null` with `note`." - }, - "finite_count": { - "description": "Beams with a finite range.", - "minimum": 0, - "type": "integer" - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame." - }, - "inf_count": { - "description": "Beams at `+inf`.", - "minimum": 0, - "type": "integer" - }, - "nan_count": { - "description": "Beams at `nan`.", - "minimum": 0, - "type": "integer" - }, - "neg_inf_count": { - "description": "Beams at `-inf`.", - "minimum": 0, - "type": "integer" - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when the summary is partial; else `null`." - }, - "out_of_range_count": { - "description": "Finite ranges outside `range_min`..`range_max` (not valid).", - "minimum": 0, - "type": "integer" - }, - "range_max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Largest valid range, m." - }, - "range_min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Smallest valid range, m." - }, - "sectors": { - "$ref": "#/$defs/ScanSectors", - "description": "Closest valid return per sector." - }, - "summary_type": { - "const": "laser_scan", - "type": "string" - } - }, - "required": [ - "summary_type", - "frame_id", - "beam_count", - "angle_min", - "angle_max", - "angle_increment", - "range_min", - "range_max", - "finite_count", - "inf_count", - "neg_inf_count", - "nan_count", - "out_of_range_count", - "closest_obstacle", - "sectors" - ], - "type": "object" -} - removed
Output schema / $defs / MessageSample / properties / received_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / received_ns / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / received_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / MessageSample / properties / recorded_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / recorded_ns / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / recorded_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / MessageSample / properties / summary / anyOfRemoved value: -[ - { - "discriminator": { - "mapping": { - "image": "#/$defs/ImageSummary", - "imu": "#/$defs/ImuSummary", - "laser_scan": "#/$defs/LaserScanSummary", - "odometry": "#/$defs/OdometrySummary", - "point_cloud2": "#/$defs/PointCloud2Summary" - }, - "propertyName": "summary_type" - }, - "oneOf": [ - { - "$ref": "#/$defs/LaserScanSummary" - }, - { - "$ref": "#/$defs/OdometrySummary" - }, - { - "$ref": "#/$defs/ImuSummary" - }, - { - "$ref": "#/$defs/ImageSummary" - }, - { - "$ref": "#/$defs/PointCloud2Summary" - } - ] - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / summary / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / summary / propertiesAdded value: +{ + "summary_type": { + "enum": [ + "laser_scan", + "odometry", + "imu", + "image", + "point_cloud2" + ], + "type": "string" + } +} - added
Output schema / $defs / MessageSample / properties / summary / requiredAdded value: +[ + "summary_type" +] - added
Output schema / $defs / MessageSample / properties / summary / typeAdded value: +[ + "object", + "null" +] - removed
Output schema / $defs / OdometrySummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angular_z": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`twist.twist.angular.z`, rad/s." - }, - "child_frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`child_frame_id` (frame of the twist)." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` (frame of the pose)." - }, - "linear_speed": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `twist.twist.linear`, m/s." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when a value is missing; else `null`." - }, - "position_x": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.x`, m." - }, - "position_y": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.y`, m." - }, - "position_z": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.z`, m." - }, - "summary_type": { - "const": "odometry", - "type": "string" - }, - "yaw": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Heading from the pose quaternion, radians." - } - }, - "required": [ - "summary_type", - "frame_id", - "child_frame_id", - "linear_speed", - "angular_z", - "position_x", - "position_y", - "position_z", - "yaw" - ], - "type": "object" -} - removed
Output schema / $defs / PointCloud2SummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "fields": { - "description": "Point layout.", - "items": { - "$ref": "#/$defs/PointFieldSummary" - }, - "type": "array" - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the cloud." - }, - "height": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Rows (1: unorganized)." - }, - "is_dense": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "True when no point is invalid." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when partial; else `null`." - }, - "point_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "`width` times `height`." - }, - "point_step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Bytes per point." - }, - "row_step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Bytes per row." - }, - "summary_type": { - "const": "point_cloud2", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Points per row." - } - }, - "required": [ - "summary_type", - "frame_id", - "width", - "height", - "point_count", - "point_step", - "row_step", - "is_dense", - "fields" - ], - "type": "object" -} - removed
Output schema / $defs / PointFieldSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Elements per point." - }, - "datatype": { - "description": "Element type, e.g. `FLOAT32`.", - "type": "string" - }, - "name": { - "description": "Field name, e.g. `x`.", - "type": "string" - }, - "offset": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Byte offset in a point." - } - }, - "required": [ - "name", - "datatype", - "offset", - "count" - ], - "type": "object" -} - removed
Output schema / $defs / ScanReturnRemoved value: -{ - "additionalProperties": false, - "properties": { - "beam_index": { - "description": "Index in `ranges`.", - "minimum": 0, - "type": "integer" - }, - "bearing": { - "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.", - "type": "number" - }, - "range": { - "description": "Distance, meters.", - "type": "number" - } - }, - "required": [ - "range", - "bearing", - "beam_index" - ], - "type": "object" -} - removed
Output schema / $defs / ScanSectorRemoved value: -{ - "additionalProperties": false, - "properties": { - "beam_count": { - "description": "Beams inside the sector (0: not covered).", - "minimum": 0, - "type": "integer" - }, - "closest": { - "anyOf": [ - { - "$ref": "#/$defs/ScanReturn" - }, - { - "type": "null" - } - ], - "description": "Closest valid return in the sector; `null` with `note`." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Why `closest` is `null`; `null` when it is set." - } - }, - "required": [ - "beam_count", - "closest" - ], - "type": "object" -} - removed
Output schema / $defs / ScanSectorsRemoved value: -{ - "additionalProperties": false, - "properties": { - "front": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings within +-45 degrees of +x." - }, - "left": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings from 45 to 135 degrees." - }, - "rear": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings beyond +-135 degrees." - }, - "right": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings from -135 to -45 degrees." - } - }, - "required": [ - "front", - "left", - "right", - "rear" - ], - "type": "object" -} - added
Output schema / $defs / TopicRate / properties / clock_basisAdded value: +{ + "description": "Stamp clock: `wall` (stamps are dates), `sim` (since a start), `none` (no stamps), `mixed` (several kinds, e.g. two publishers).", + "enum": [ + "wall", + "sim", + "none", + "mixed" + ], + "type": "string" +} - removed
Output schema / $defs / TopicRate / properties / interval_cv / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / interval_cv / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / interval_median_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / interval_median_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / max_gap_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / max_gap_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / mean_interval_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / mean_interval_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / observed_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / observed_frequency_hz / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / sim_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / sim_frequency_hz / typeAdded value: +[ + "number", + "null" +] - added
Output schema / $defs / TopicRate / properties / stamp_age_median_sAdded value: +{ + "description": "Median of arrival minus stamp, s, if `wall`; negative = publisher ahead.", + "type": [ + "number", + "null" + ] +} - added
Output schema / $defs / TopicRate / properties / stamp_age_p95_sAdded value: +{ + "description": "95th percentile of that age (max under 20 messages).", + "type": [ + "number", + "null" + ] +} - added
Output schema / $defs / TopicRate / properties / stamp_regression_countAdded value: +{ + "description": "Times a stamp went back versus the previous one.", + "minimum": 0, + "type": "integer" +} - removed
Output schema / $defs / TopicRate / properties / trailing_gap_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / trailing_gap_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / rate / defaultRemoved value: -null
- Changed
sample_messages44 fields changed- added
Input schema / properties / min_log_levelAdded value: +{ + "anyOf": [ + { + "enum": [ + "debug", + "info", + "warn", + "error", + "fatal" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "`/rosout` (`rcl_interfaces/msg/Log`) only: keep messages at or above this level (debug 10, info 20, warn 30, error 40, fatal 50). Other topics: error." +} - added
Input schema / properties / nodeAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "`/rosout` only: keep logs of this node and its child loggers, e.g. `/robot1/driver` (also `driver`, `robot1.driver`). Exact name, never a prefix. Other topics: error." +} - removed
Output schema / $defs / ImageSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "data_length": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixel buffer size in bytes (see `data_length_basis`)." - }, - "data_length_basis": { - "anyOf": [ - { - "enum": [ - "payload", - "step_x_height" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)." - }, - "encoding": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Pixel encoding, e.g. `rgb8`." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the camera." - }, - "height": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixels." - }, - "is_bigendian": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "Big-endian pixels." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when partial; else `null`." - }, - "step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Row length, bytes." - }, - "summary_type": { - "const": "image", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Pixels." - } - }, - "required": [ - "summary_type", - "frame_id", - "width", - "height", - "encoding", - "step", - "is_bigendian", - "data_length", - "data_length_basis" - ], - "type": "object" -} - removed
Output schema / $defs / ImuSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angular_velocity_norm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `angular_velocity`, rad/s." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the IMU." - }, - "linear_acceleration_norm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `linear_acceleration`, m/s^2." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when a value is unusable; else `null`." - }, - "orientation_valid": { - "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.", - "type": "boolean" - }, - "pitch": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about y, radians." - }, - "roll": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about x, radians." - }, - "summary_type": { - "const": "imu", - "type": "string" - }, - "yaw": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Rotation about z, radians." - } - }, - "required": [ - "summary_type", - "frame_id", - "orientation_valid", - "roll", - "pitch", - "yaw", - "angular_velocity_norm", - "linear_acceleration_norm" - ], - "type": "object" -} - removed
Output schema / $defs / LaserScanSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angle_increment": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Angle between beams, rad." - }, - "angle_max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Bearing of the last beam, rad." - }, - "angle_min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Bearing of beam 0, rad." - }, - "beam_count": { - "description": "Beams in `ranges`.", - "minimum": 0, - "type": "integer" - }, - "closest_obstacle": { - "anyOf": [ - { - "$ref": "#/$defs/ScanReturn" - }, - { - "type": "null" - } - ], - "description": "Closest valid return of the scan; `null` with `note`." - }, - "finite_count": { - "description": "Beams with a finite range.", - "minimum": 0, - "type": "integer" - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame." - }, - "inf_count": { - "description": "Beams at `+inf`.", - "minimum": 0, - "type": "integer" - }, - "nan_count": { - "description": "Beams at `nan`.", - "minimum": 0, - "type": "integer" - }, - "neg_inf_count": { - "description": "Beams at `-inf`.", - "minimum": 0, - "type": "integer" - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when the summary is partial; else `null`." - }, - "out_of_range_count": { - "description": "Finite ranges outside `range_min`..`range_max` (not valid).", - "minimum": 0, - "type": "integer" - }, - "range_max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Largest valid range, m." - }, - "range_min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Smallest valid range, m." - }, - "sectors": { - "$ref": "#/$defs/ScanSectors", - "description": "Closest valid return per sector." - }, - "summary_type": { - "const": "laser_scan", - "type": "string" - } - }, - "required": [ - "summary_type", - "frame_id", - "beam_count", - "angle_min", - "angle_max", - "angle_increment", - "range_min", - "range_max", - "finite_count", - "inf_count", - "neg_inf_count", - "nan_count", - "out_of_range_count", - "closest_obstacle", - "sectors" - ], - "type": "object" -} - removed
Output schema / $defs / MessageSample / properties / received_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / received_ns / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / received_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / MessageSample / properties / recorded_ns / anyOfRemoved value: -[ - { - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / recorded_ns / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / recorded_ns / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / $defs / MessageSample / properties / summary / anyOfRemoved value: -[ - { - "discriminator": { - "mapping": { - "image": "#/$defs/ImageSummary", - "imu": "#/$defs/ImuSummary", - "laser_scan": "#/$defs/LaserScanSummary", - "odometry": "#/$defs/OdometrySummary", - "point_cloud2": "#/$defs/PointCloud2Summary" - }, - "propertyName": "summary_type" - }, - "oneOf": [ - { - "$ref": "#/$defs/LaserScanSummary" - }, - { - "$ref": "#/$defs/OdometrySummary" - }, - { - "$ref": "#/$defs/ImuSummary" - }, - { - "$ref": "#/$defs/ImageSummary" - }, - { - "$ref": "#/$defs/PointCloud2Summary" - } - ] - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / summary / defaultRemoved value: -null - added
Output schema / $defs / MessageSample / properties / summary / propertiesAdded value: +{ + "summary_type": { + "enum": [ + "laser_scan", + "odometry", + "imu", + "image", + "point_cloud2" + ], + "type": "string" + } +} - added
Output schema / $defs / MessageSample / properties / summary / requiredAdded value: +[ + "summary_type" +] - added
Output schema / $defs / MessageSample / properties / summary / typeAdded value: +[ + "object", + "null" +] - removed
Output schema / $defs / OdometrySummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "angular_z": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`twist.twist.angular.z`, rad/s." - }, - "child_frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`child_frame_id` (frame of the twist)." - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` (frame of the pose)." - }, - "linear_speed": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Norm of `twist.twist.linear`, m/s." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when a value is missing; else `null`." - }, - "position_x": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.x`, m." - }, - "position_y": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.y`, m." - }, - "position_z": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "`pose.pose.position.z`, m." - }, - "summary_type": { - "const": "odometry", - "type": "string" - }, - "yaw": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Heading from the pose quaternion, radians." - } - }, - "required": [ - "summary_type", - "frame_id", - "child_frame_id", - "linear_speed", - "angular_z", - "position_x", - "position_y", - "position_z", - "yaw" - ], - "type": "object" -} - removed
Output schema / $defs / PointCloud2SummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "fields": { - "description": "Point layout.", - "items": { - "$ref": "#/$defs/PointFieldSummary" - }, - "type": "array" - }, - "frame_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "`header.frame_id` of the cloud." - }, - "height": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Rows (1: unorganized)." - }, - "is_dense": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "True when no point is invalid." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "One sentence when partial; else `null`." - }, - "point_count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "`width` times `height`." - }, - "point_step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Bytes per point." - }, - "row_step": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Bytes per row." - }, - "summary_type": { - "const": "point_cloud2", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Points per row." - } - }, - "required": [ - "summary_type", - "frame_id", - "width", - "height", - "point_count", - "point_step", - "row_step", - "is_dense", - "fields" - ], - "type": "object" -} - removed
Output schema / $defs / PointFieldSummaryRemoved value: -{ - "additionalProperties": false, - "properties": { - "count": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Elements per point." - }, - "datatype": { - "description": "Element type, e.g. `FLOAT32`.", - "type": "string" - }, - "name": { - "description": "Field name, e.g. `x`.", - "type": "string" - }, - "offset": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "Byte offset in a point." - } - }, - "required": [ - "name", - "datatype", - "offset", - "count" - ], - "type": "object" -} - removed
Output schema / $defs / ScanReturnRemoved value: -{ - "additionalProperties": false, - "properties": { - "beam_index": { - "description": "Index in `ranges`.", - "minimum": 0, - "type": "integer" - }, - "bearing": { - "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.", - "type": "number" - }, - "range": { - "description": "Distance, meters.", - "type": "number" - } - }, - "required": [ - "range", - "bearing", - "beam_index" - ], - "type": "object" -} - removed
Output schema / $defs / ScanSectorRemoved value: -{ - "additionalProperties": false, - "properties": { - "beam_count": { - "description": "Beams inside the sector (0: not covered).", - "minimum": 0, - "type": "integer" - }, - "closest": { - "anyOf": [ - { - "$ref": "#/$defs/ScanReturn" - }, - { - "type": "null" - } - ], - "description": "Closest valid return in the sector; `null` with `note`." - }, - "note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Why `closest` is `null`; `null` when it is set." - } - }, - "required": [ - "beam_count", - "closest" - ], - "type": "object" -} - removed
Output schema / $defs / ScanSectorsRemoved value: -{ - "additionalProperties": false, - "properties": { - "front": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings within +-45 degrees of +x." - }, - "left": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings from 45 to 135 degrees." - }, - "rear": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings beyond +-135 degrees." - }, - "right": { - "$ref": "#/$defs/ScanSector", - "description": "Bearings from -135 to -45 degrees." - } - }, - "required": [ - "front", - "left", - "right", - "rear" - ], - "type": "object" -} - added
Output schema / $defs / TopicRate / properties / clock_basisAdded value: +{ + "description": "Stamp clock: `wall` (stamps are dates), `sim` (since a start), `none` (no stamps), `mixed` (several kinds, e.g. two publishers).", + "enum": [ + "wall", + "sim", + "none", + "mixed" + ], + "type": "string" +} - removed
Output schema / $defs / TopicRate / properties / interval_cv / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / interval_cv / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / interval_median_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / interval_median_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / max_gap_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / max_gap_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / mean_interval_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / mean_interval_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / observed_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / observed_frequency_hz / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / $defs / TopicRate / properties / sim_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / sim_frequency_hz / typeAdded value: +[ + "number", + "null" +] - added
Output schema / $defs / TopicRate / properties / stamp_age_median_sAdded value: +{ + "description": "Median of arrival minus stamp, s, if `wall`; negative = publisher ahead.", + "type": [ + "number", + "null" + ] +} - added
Output schema / $defs / TopicRate / properties / stamp_age_p95_sAdded value: +{ + "description": "95th percentile of that age (max under 20 messages).", + "type": [ + "number", + "null" + ] +} - added
Output schema / $defs / TopicRate / properties / stamp_regression_countAdded value: +{ + "description": "Times a stamp went back versus the previous one.", + "minimum": 0, + "type": "integer" +} - removed
Output schema / $defs / TopicRate / properties / trailing_gap_s / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / $defs / TopicRate / properties / trailing_gap_s / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / rate / defaultRemoved value: -null
- Changed
topic_metrics25 fields changed- removed
Output schema / properties / declared_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - removed
Output schema / properties / declared_frequency_hz / defaultRemoved value: -null - added
Output schema / properties / declared_frequency_hz / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / latency_available / defaultRemoved value: -false - removed
Output schema / properties / latency_ns_p50 / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / latency_ns_p50 / defaultRemoved value: -null - added
Output schema / properties / latency_ns_p50 / minimumAdded value: +0 - added
Output schema / properties / latency_ns_p50 / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / latency_ns_p95 / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / latency_ns_p95 / defaultRemoved value: -null - added
Output schema / properties / latency_ns_p95 / minimumAdded value: +0 - added
Output schema / properties / latency_ns_p95 / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / latency_ns_p99 / anyOfRemoved value: -[ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } -] - removed
Output schema / properties / latency_ns_p99 / defaultRemoved value: -null - added
Output schema / properties / latency_ns_p99 / minimumAdded value: +0 - added
Output schema / properties / latency_ns_p99 / typeAdded value: +[ + "integer", + "null" +] - removed
Output schema / properties / note / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / properties / note / defaultRemoved value: -null - added
Output schema / properties / note / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / observed_frequency_hz / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - removed
Output schema / properties / observed_frequency_hz / defaultRemoved value: -null - added
Output schema / properties / observed_frequency_hz / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / sequence_gaps_count / defaultRemoved value: -0 - removed
Output schema / properties / sequence_numbers_available / defaultRemoved value: -false - removed
Output schema / properties / status / defaultRemoved value: -"ok"
14 tool updates
v0.7.1- Changed
analyze_bag42 fields changed- changed
Input schema / properties / path / descriptionPrevious value: -"Path to a bag: a file ending in `.mcap` or `.db3`, or a `rosbag2_*` directory. `peek_bag_samples` also reads ROS 1 `.bag` files; `analyze_bag` does not (`ros2 bag info` cannot open them). Leading/trailing whitespace is stripped. Null bytes and otherwise malformed filesystem paths are rejected. Existence and bag format are validated by the live adapter (mock mode accepts any well-formed path)."New value: +"Bag path: a `.mcap` or `.db3` file or a `rosbag2_*` directory (`peek_bag_samples` also reads ROS 1 `.bag`; `analyze_bag` does not). Existence and format are checked live; mock accepts any well-formed path." - removed
Input schema / properties / path / titleRemoved value: -"Path" - removed
Input schema / titleRemoved value: -"analyze_bagArguments" - removed
Output schema / $defs / BagTopicStats / descriptionRemoved value: -"Per-topic statistics inside a bag analysis result." - changed
Output schema / $defs / BagTopicStats / properties / first_timestamp_ns / descriptionPrevious value: -"Recorded time of the first message on this topic, in nanoseconds (bag receive time, normally epoch). `null` when the bag was summarized from `ros2 bag info` text only, or the topic has no messages."New value: +"Recorded ns of the first message; `null` for text-only summaries or no messages." - removed
Output schema / $defs / BagTopicStats / properties / first_timestamp_ns / titleRemoved value: -"First Timestamp Ns" - changed
Output schema / $defs / BagTopicStats / properties / frequency_basis / descriptionPrevious value: -"How `frequency_hz` was computed: `topic_span` (per-topic first and last message times, the accurate one) or `bag_duration` (count divided by whole-bag duration, used when per-topic times could not be read). `null` when there is no rate."New value: +"`topic_span` (accurate) or `bag_duration` (count over the whole bag, understates late topics); `null` without a rate." - removed
Output schema / $defs / BagTopicStats / properties / frequency_basis / titleRemoved value: -"Frequency Basis" - changed
Output schema / $defs / BagTopicStats / properties / frequency_hz / descriptionPrevious value: -"Average publish rate in Hz. With `frequency_basis` `topic_span` it is `(message_count - 1) / (last - first message time)` for this topic; with `bag_duration` it is `message_count / bag duration`, which understates the rate of a topic that started late or stopped early. `null` when fewer than 2 messages, a zero time span, or the topic is `latched` and all its messages fall within 1 second (a start-up burst, not a rate). A latched topic published over a longer span keeps its rate. Not a periodic rate for event-driven topics such as `/parameter_events`."New value: +"Average Hz: (count - 1) over the topic's own time span (see `frequency_basis`); `null` under 2 messages, a zero span, or a latched start-up burst (all within 1 s)." - removed
Output schema / $defs / BagTopicStats / properties / frequency_hz / titleRemoved value: -"Frequency Hz" - added
Output schema / $defs / BagTopicStats / properties / kindAdded value: +{ + "default": "user", + "description": "`user` (data, incl. `/tf` and `/clock`), `rosbag2_internal` or `ros_builtin` (e.g. `/rosout`).", + "enum": [ + "user", + "rosbag2_internal", + "ros_builtin" + ], + "type": "string" +} - changed
Output schema / $defs / BagTopicStats / properties / last_timestamp_ns / descriptionPrevious value: -"Recorded time of the last message on this topic; same rules as `first_timestamp_ns`."New value: +"Recorded ns of the last message; same rules." - removed
Output schema / $defs / BagTopicStats / properties / last_timestamp_ns / titleRemoved value: -"Last Timestamp Ns" - changed
Output schema / $defs / BagTopicStats / properties / latched / descriptionPrevious value: -"True when a publisher recorded this topic with transient_local durability (late joiners get the last samples, e.g. `/tf_static`, `/rosout`). Such a topic often publishes a start-up burst; when all its messages fall within 1 second `frequency_hz` is `null`. `false` when every recorded publisher was volatile; `null` when the bag carries no QoS information (ROS 1 bags report their latching flag)."New value: +"True if recorded with transient_local durability, false if volatile, `null` without QoS information." - removed
Output schema / $defs / BagTopicStats / properties / latched / titleRemoved value: -"Latched" - removed
Output schema / $defs / BagTopicStats / properties / message_count / titleRemoved value: -"Message Count" - removed
Output schema / $defs / BagTopicStats / properties / message_type / descriptionRemoved value: -"ROS2 message type recorded for this topic." - removed
Output schema / $defs / BagTopicStats / properties / message_type / titleRemoved value: -"Message Type" - removed
Output schema / $defs / BagTopicStats / properties / name / descriptionRemoved value: -"Fully qualified topic name as recorded in the bag." - removed
Output schema / $defs / BagTopicStats / properties / name / titleRemoved value: -"Name" - removed
Output schema / $defs / BagTopicStats / titleRemoved value: -"BagTopicStats" - removed
Output schema / $defs / ParticipantInfoRemoved value: -{ - "additionalProperties": false, - "description": "DDS participant discovered on the configured domain.\n\nCarries identity (`guid`, `name`, `vendor`) plus lifecycle fields\n(`first_seen_ns`, `last_seen_ns`, `status`, `seen_count`).", - "properties": { - "announced_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Nanoseconds since epoch (DDS source timestamp of the participant's most recent announcement, taken from the announcing side's clock; the local clock when the announcer sent none). `None` when the backend does not expose it. Distinct from `first_seen_ns` / `last_seen_ns`, which are TopicForge's own local clock.", - "title": "Announced Ns" - }, - "domain_id": { - "description": "DDS domain id the participant is bound to.", - "maximum": 232, - "minimum": 0, - "title": "Domain Id", - "type": "integer" - }, - "first_seen_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Wall-clock timestamp (nanoseconds since epoch) of the **first** discovery sample TopicForge observed for this participant. `None` when the adapter does not track lifecycle.", - "title": "First Seen Ns" - }, - "guid": { - "description": "DDS Global Unique Identifier of the participant: hex string, stable across discovery events within a single deployment.", - "title": "Guid", - "type": "string" - }, - "hostname": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Hostname announced in discovery (Cyclone `__Hostname` property). `None` when the vendor does not announce it.", - "title": "Hostname" - }, - "is_observer": { - "default": false, - "description": "True for TopicForge's own read-only observer participant, which joins the domain to watch it and appears in this list; false for every other participant.", - "title": "Is Observer", - "type": "boolean" - }, - "last_seen_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Wall-clock timestamp (nanoseconds since epoch) of the **most recent** discovery sample observed. Used by `participant_events` to distinguish currently-active participants from those that left the bus. `None` when the adapter does not track lifecycle.", - "title": "Last Seen Ns" - }, - "lost_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Nanoseconds since epoch of the moment the participant left, once `status` is `left`; `None` while it is active. How precise it is depends on `lost_time_source`. It is an upper bound of when the participant died: after a clean shutdown it is the exact leave time, after a crash it is when the lease expired, and the two cannot be told apart. The process died at or before `lost_ns`, and at most one lease earlier (10 s Cyclone default, 20 s Fast, 100 s RTI).", - "title": "Lost Ns" - }, - "lost_time_source": { - "anyOf": [ - { - "enum": [ - "dds_source_timestamp", - "observed_local" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Where `lost_ns` comes from: `dds_source_timestamp` (timestamp carried by the discovery dispose) or `observed_local` (the moment TopicForge noticed, the weakest). `None` while active.", - "title": "Lost Time Source" - }, - "mode_effective": { - "description": "Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`.", - "enum": [ - "mock", - "live" - ], - "title": "Mode Effective", - "type": "string" - }, - "name": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Participant name the application announced through the standard DDS EntityName QoS (PID_ENTITY_NAME in RTPS discovery), for example `lidar_driver`. `None` when the application did not set one (Dust DDS cannot) or the backend does not expose it.", - "title": "Name" - }, - "seen_count": { - "default": 1, - "description": "Number of distinct discovery samples observed for this participant across all calls to `list_participants` during this server's lifetime. Starts at 1 and increments on each observation.", - "minimum": 1, - "title": "Seen Count", - "type": "integer" - }, - "status": { - "default": "unknown", - "description": "Lifecycle status. `active` means TopicForge has a recent discovery sample for this GUID and the bus has not signalled removal. `left` means the participant was observed earlier but has since disappeared (a Fast DDS `REMOVED` callback or a Cyclone polling delta). `unknown` when the adapter does not track lifecycle.", - "enum": [ - "active", - "left", - "unknown" - ], - "title": "Status", - "type": "string" - }, - "vendor": { - "description": "DDS implementation that announced this participant, decoded from the OMG-RTPS `vendor_id` field on the discovery sample. `cyclone` (Eclipse Cyclone DDS), `fast` (eProsima Fast DDS), `rti` (RTI Connext), `rti_micro` (RTI Connext Micro), `opensplice` (ADLink OpenSplice), `opendds` (OCI OpenDDS), `coredx` (Twin Oaks CoreDX), `intercom` (Kongsberg InterCOM), `dust` (S2E Dust DDS). `mock` is reserved for synthetic fixtures. `unknown` means the vendor could not be determined: TopicForge reads it from the vendor prefix of the participant GUID, and some vendors (for example Dust DDS and RTI Connext) do not put their vendor id there, while the Cyclone Python binding does not expose the RTPS header vendor id. The participant is still reported; see `vendor_source`. Any conformant DDS-RTPS participant should appear through standard discovery; Cyclone DDS and Dust DDS have been observed on a live bus. See `docs/dds-interop-matrix.md`.", - "enum": [ - "cyclone", - "fast", - "rti", - "rti_micro", - "opensplice", - "opendds", - "coredx", - "intercom", - "dust", - "mock", - "unknown" - ], - "title": "Vendor", - "type": "string" - }, - "vendor_source": { - "default": "none", - "description": "Where `vendor` came from: `guid_prefix` when it was read from the vendor prefix of the participant GUID, `none` when the vendor is unknown (or synthetic in mock mode).", - "enum": [ - "guid_prefix", - "none" - ], - "title": "Vendor Source", - "type": "string" - } - }, - "required": [ - "guid", - "vendor", - "domain_id", - "mode_effective" - ], - "title": "ParticipantInfo", - "type": "object" -} - changed
Output schema / descriptionPrevious value: -"Structured summary of a ROS2 bag.\n\nThe fields `bag_format`, `samples_decoded_count`,\n`recording_duration_ns` and `participants_recorded` are populated only\nwhen the `rosbags`-backed reader runs; the `ros2 bag info` path leaves\nthem at their defaults."New value: +"Structured summary of a ROS2 bag." - changed
Output schema / properties / anomalies / descriptionPrevious value: -"Human-readable notes about gaps, clock jumps, or other oddities. Populated in mock mode only; live mode does not detect anomalies."New value: +"Notes on gaps or clock jumps; mock mode only." - removed
Output schema / properties / anomalies / titleRemoved value: -"Anomalies" - changed
Output schema / properties / bag_format / descriptionPrevious value: -"Concrete bag container format detected by the reader: `mcap` (Foxglove MCAP), `db3` (ROS2 rosbag2 SQLite), `bag` (ROS1 legacy chunked), or `unknown` when the reader could not classify. `None` when the bag was summarized from `ros2 bag info` text, which carries no format information."New value: +"Detected container: `mcap`, `db3`, `bag` or `unknown`; `None` for `ros2 bag info` text." - removed
Output schema / properties / bag_format / titleRemoved value: -"Bag Format" - added
Output schema / properties / duration_sAdded value: +{ + "description": "Bag duration in seconds.", + "minimum": 0, + "type": "number" +} - removed
Output schema / properties / duration_secondsRemoved value: -{ - "description": "Total bag duration, in seconds (wall clock between first and last message).", - "minimum": 0, - "title": "Duration Seconds", - "type": "number" -} - removed
Output schema / properties / message_count / titleRemoved value: -"Message Count" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - changed
Output schema / properties / note / descriptionPrevious value: -"Why the result is less detailed than usual, for example per-topic rates computed over the whole bag duration because the bag was too large to read per-topic message times. `None` when there is nothing to add."New value: +"Why the result is less detailed than usual (e.g. rates over the whole bag duration); else `None`." - removed
Output schema / properties / note / titleRemoved value: -"Note" - removed
Output schema / properties / participants_recordedRemoved value: -{ - "description": "DDS participants recorded in the bag when the container format embeds participant metadata. MCAP can carry it via channel metadata records; ROS2 `.db3` and ROS1 `.bag` generally do not. Empty list when not available, which is the common case.", - "items": { - "$ref": "#/$defs/ParticipantInfo" - }, - "title": "Participants Recorded", - "type": "array" -} - changed
Output schema / properties / path / descriptionPrevious value: -"Path to the analyzed bag, as supplied by the caller. May point to a file (`.mcap`, `.db3`, or ROS 1 `.bag` for `peek_bag_samples`) or to a `rosbag2_*` directory."New value: +"Bag path as supplied." - removed
Output schema / properties / path / titleRemoved value: -"Path" - removed
Output schema / properties / recording_duration_nsRemoved value: -{ - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Recording duration in nanoseconds when readable from the bag's index. `None` when only `ros2 bag info` text was parsed; `duration_seconds` (float) is the always-populated fallback that downstream LLM consumers should prefer when this is `None`.", - "title": "Recording Duration Ns" -} - removed
Output schema / properties / samples_decoded_countRemoved value: -{ - "default": 0, - "description": "Total decoded sample count across all topics produced by the bag reader. `0` when the reader only parsed metadata or when `rosbags` is not installed on the host. Use `peek_bag_samples` to pull the actual sample payloads for a specific topic.", - "minimum": 0, - "title": "Samples Decoded Count", - "type": "integer" -} - removed
Output schema / properties / storage_format / titleRemoved value: -"Storage Format" - removed
Output schema / properties / topics / titleRemoved value: -"Topics" - changed
Output schema / requiredPrevious value: -[ - "path", - "duration_seconds", - "message_count", - "topics", - "mode_effective" -]New value: +[ + "path", + "duration_s", + "message_count", + "topics", + "mode_effective" +]
- Changed
detect_qos_mismatches110 fields changed- changed
Input schema / properties / topic / descriptionPrevious value: -"Optional DDS topic name to scope the scan to: a bare name such as `scan` or a ROS 2 mangled name such as `rt/scan`. Omit to scan all topics."New value: +"Optional DDS topic to scope the scan to: `scan` or `rt/scan`. Omit to scan all topics." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - removed
Input schema / titleRemoved value: -"detect_qos_mismatchesArguments" - removed
Output schema / $defs / MatchedPair / descriptionRemoved value: -"A reader and a writer that DDS will connect given their announced QoS." - changed
Output schema / $defs / MatchedPair / properties / late_joiner / descriptionPrevious value: -"True when the writer is VOLATILE and the reader was announced more than 1 s after it, both on the same host: samples published before the reader joined are not delivered to it. Normal for a VOLATILE writer, not a fault."New value: +"True for a VOLATILE writer whose reader joined over 1 s later on the same host: earlier samples are not delivered. Normal." - removed
Output schema / $defs / MatchedPair / properties / late_joiner / titleRemoved value: -"Late Joiner" - removed
Output schema / $defs / MatchedPair / properties / late_joiner_note / titleRemoved value: -"Late Joiner Note" - removed
Output schema / $defs / MatchedPair / properties / reader_guid / descriptionRemoved value: -"GUID of the reader endpoint." - removed
Output schema / $defs / MatchedPair / properties / reader_guid / titleRemoved value: -"Reader Guid" - removed
Output schema / $defs / MatchedPair / properties / reader_participant_guid / descriptionRemoved value: -"GUID of the reader's participant." - removed
Output schema / $defs / MatchedPair / properties / reader_participant_guid / titleRemoved value: -"Reader Participant Guid" - removed
Output schema / $defs / MatchedPair / properties / reader_participant_name / descriptionRemoved value: -"Announced name of the reader's participant." - removed
Output schema / $defs / MatchedPair / properties / reader_participant_name / titleRemoved value: -"Reader Participant Name" - removed
Output schema / $defs / MatchedPair / properties / topic / descriptionRemoved value: -"Topic name both endpoints use." - removed
Output schema / $defs / MatchedPair / properties / topic / titleRemoved value: -"Topic" - removed
Output schema / $defs / MatchedPair / properties / type_name / descriptionRemoved value: -"Type name both announced." - removed
Output schema / $defs / MatchedPair / properties / type_name / titleRemoved value: -"Type Name" - removed
Output schema / $defs / MatchedPair / properties / writer_guid / descriptionRemoved value: -"GUID of the writer endpoint." - removed
Output schema / $defs / MatchedPair / properties / writer_guid / titleRemoved value: -"Writer Guid" - removed
Output schema / $defs / MatchedPair / properties / writer_participant_guid / descriptionRemoved value: -"GUID of the writer's participant." - removed
Output schema / $defs / MatchedPair / properties / writer_participant_guid / titleRemoved value: -"Writer Participant Guid" - removed
Output schema / $defs / MatchedPair / properties / writer_participant_name / descriptionRemoved value: -"Announced name of the writer's participant." - removed
Output schema / $defs / MatchedPair / properties / writer_participant_name / titleRemoved value: -"Writer Participant Name" - removed
Output schema / $defs / MatchedPair / titleRemoved value: -"MatchedPair" - removed
Output schema / $defs / MismatchReport / descriptionRemoved value: -"A single reader/writer QoS incompatibility detected on a topic.\n\nOnly pairs that share a partition and a type name are reported here: those\nseparated by partition or type are `NotMatchedPair`s in the same scan." - changed
Output schema / $defs / MismatchReport / properties / details / descriptionPrevious value: -"Requested and offered value and the failed rule, one entry per policy."New value: +"Requested and offered value and the failed rule, per policy." - removed
Output schema / $defs / MismatchReport / properties / details / titleRemoved value: -"Details" - changed
Output schema / $defs / MismatchReport / properties / incompatible_policies / descriptionPrevious value: -"Names of the QoS policies that block communication or risk degradation: see `details` for the compared values."New value: +"Blocking or risky policies; values in `details`." - removed
Output schema / $defs / MismatchReport / properties / incompatible_policies / titleRemoved value: -"Incompatible Policies" - removed
Output schema / $defs / MismatchReport / properties / mode_effectiveRemoved value: -{ - "description": "Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`.", - "enum": [ - "mock", - "live" - ], - "title": "Mode Effective", - "type": "string" -} - removed
Output schema / $defs / MismatchReport / properties / reader_guid / descriptionRemoved value: -"GUID of the reader endpoint involved in the mismatch, if known." - removed
Output schema / $defs / MismatchReport / properties / reader_guid / titleRemoved value: -"Reader Guid" - removed
Output schema / $defs / MismatchReport / properties / reader_participant_guid / descriptionRemoved value: -"GUID of the participant that owns the reader, if known." - removed
Output schema / $defs / MismatchReport / properties / reader_participant_guid / titleRemoved value: -"Reader Participant Guid" - removed
Output schema / $defs / MismatchReport / properties / reader_participant_name / descriptionRemoved value: -"Announced name of the reader's participant, if it set one." - removed
Output schema / $defs / MismatchReport / properties / reader_participant_name / titleRemoved value: -"Reader Participant Name" - removed
Output schema / $defs / MismatchReport / properties / reader_type_name / descriptionRemoved value: -"Type name the reader announced." - removed
Output schema / $defs / MismatchReport / properties / reader_type_name / titleRemoved value: -"Reader Type Name" - changed
Output schema / $defs / MismatchReport / properties / severity / descriptionPrevious value: -"`incompatible` means communication is definitely blocked; `risky` means it may degrade but is not strictly blocked by the DDS spec (History only). Useful for an LLM to triage user-facing advice."New value: +"`incompatible` (blocked) or `risky` (may degrade; History only)." - removed
Output schema / $defs / MismatchReport / properties / severity / titleRemoved value: -"Severity" - added
Output schema / $defs / MismatchReport / properties / suggested_fixesAdded value: +{ + "description": "Fixes as text for a human to apply, never applied; empty when none.", + "items": { + "$ref": "#/$defs/SuggestedFix" + }, + "type": "array" +} - removed
Output schema / $defs / MismatchReport / properties / topic / descriptionRemoved value: -"Topic name where the mismatch was detected." - removed
Output schema / $defs / MismatchReport / properties / topic / titleRemoved value: -"Topic" - changed
Output schema / $defs / MismatchReport / properties / unchecked / descriptionPrevious value: -"Policies that could not be compared for this pair because one side did not announce a value."New value: +"Policies not compared because one side announced no value." - removed
Output schema / $defs / MismatchReport / properties / unchecked / titleRemoved value: -"Unchecked" - removed
Output schema / $defs / MismatchReport / properties / writer_guid / descriptionRemoved value: -"GUID of the writer endpoint involved in the mismatch, if known." - removed
Output schema / $defs / MismatchReport / properties / writer_guid / titleRemoved value: -"Writer Guid" - removed
Output schema / $defs / MismatchReport / properties / writer_participant_guid / descriptionRemoved value: -"GUID of the participant that owns the writer, if known." - removed
Output schema / $defs / MismatchReport / properties / writer_participant_guid / titleRemoved value: -"Writer Participant Guid" - removed
Output schema / $defs / MismatchReport / properties / writer_participant_name / descriptionRemoved value: -"Announced name of the writer's participant, if it set one." - removed
Output schema / $defs / MismatchReport / properties / writer_participant_name / titleRemoved value: -"Writer Participant Name" - removed
Output schema / $defs / MismatchReport / properties / writer_type_name / descriptionRemoved value: -"Type name the writer announced." - removed
Output schema / $defs / MismatchReport / properties / writer_type_name / titleRemoved value: -"Writer Type Name" - changed
Output schema / $defs / MismatchReport / requiredPrevious value: -[ - "topic", - "incompatible_policies", - "severity", - "mode_effective" -]New value: +[ + "topic", + "incompatible_policies", + "severity" +] - removed
Output schema / $defs / MismatchReport / titleRemoved value: -"MismatchReport" - removed
Output schema / $defs / NotMatchedPair / descriptionRemoved value: -"A reader and a writer on one topic that DDS will not match, whatever their QoS." - removed
Output schema / $defs / NotMatchedPair / properties / detail / titleRemoved value: -"Detail" - changed
Output schema / $defs / NotMatchedPair / properties / latent_incompatible_policies / descriptionPrevious value: -"RxO policies that would ALSO be incompatible once the partition / type issue is fixed (same shape as `MismatchReport.details`). Empty when the QoS of the two endpoints would be compatible, or could not be compared."New value: +"RxO policies that would ALSO be incompatible once the partition or type is fixed (shape of `MismatchReport.details`)." - removed
Output schema / $defs / NotMatchedPair / properties / latent_incompatible_policies / titleRemoved value: -"Latent Incompatible Policies" - removed
Output schema / $defs / NotMatchedPair / properties / reader_guid / descriptionRemoved value: -"GUID of the reader endpoint." - removed
Output schema / $defs / NotMatchedPair / properties / reader_guid / titleRemoved value: -"Reader Guid" - removed
Output schema / $defs / NotMatchedPair / properties / reader_participant_guid / descriptionRemoved value: -"GUID of the reader's participant." - removed
Output schema / $defs / NotMatchedPair / properties / reader_participant_guid / titleRemoved value: -"Reader Participant Guid" - removed
Output schema / $defs / NotMatchedPair / properties / reader_participant_name / descriptionRemoved value: -"Announced name of the reader's participant." - removed
Output schema / $defs / NotMatchedPair / properties / reader_participant_name / titleRemoved value: -"Reader Participant Name" - changed
Output schema / $defs / NotMatchedPair / properties / reason / descriptionPrevious value: -"`partition`: no reader partition matches a writer partition. `type_name`: the endpoints announced different type names. The RxO QoS rules are not evaluated for such a pair."New value: +"`partition` (none in common) or `type_name` (different types); QoS rules are not evaluated." - removed
Output schema / $defs / NotMatchedPair / properties / reason / titleRemoved value: -"Reason" - added
Output schema / $defs / NotMatchedPair / properties / suggested_fixesAdded value: +{ + "description": "Fixes proposed as text for a human to apply; TopicForge never applies them. Only for a `partition` split, and only on the first pair of identical splits on a topic. Empty when it proposes none for this finding.", + "items": { + "$ref": "#/$defs/SuggestedFix" + }, + "type": "array" +} - removed
Output schema / $defs / NotMatchedPair / properties / topic / descriptionRemoved value: -"Topic name both endpoints use." - removed
Output schema / $defs / NotMatchedPair / properties / topic / titleRemoved value: -"Topic" - removed
Output schema / $defs / NotMatchedPair / properties / writer_guid / descriptionRemoved value: -"GUID of the writer endpoint." - removed
Output schema / $defs / NotMatchedPair / properties / writer_guid / titleRemoved value: -"Writer Guid" - removed
Output schema / $defs / NotMatchedPair / properties / writer_participant_guid / descriptionRemoved value: -"GUID of the writer's participant." - removed
Output schema / $defs / NotMatchedPair / properties / writer_participant_guid / titleRemoved value: -"Writer Participant Guid" - removed
Output schema / $defs / NotMatchedPair / properties / writer_participant_name / descriptionRemoved value: -"Announced name of the writer's participant." - removed
Output schema / $defs / NotMatchedPair / properties / writer_participant_name / titleRemoved value: -"Writer Participant Name" - removed
Output schema / $defs / NotMatchedPair / titleRemoved value: -"NotMatchedPair" - removed
Output schema / $defs / PolicyMismatch / descriptionRemoved value: -"One QoS policy of a reader/writer pair, with the values that were compared." - removed
Output schema / $defs / PolicyMismatch / properties / offered / descriptionRemoved value: -"What the writer offers, same format as `requested`." - removed
Output schema / $defs / PolicyMismatch / properties / offered / titleRemoved value: -"Offered" - changed
Output schema / $defs / PolicyMismatch / properties / policy / descriptionPrevious value: -"Policy name: `Reliability`, `Durability`, `Deadline`, `Liveliness`, `LatencyBudget`, `Ownership`, `DestinationOrder`, `DataRepresentation` or `History`."New value: +"Policy name, e.g. `Reliability`, `Deadline`, `Ownership`, `History`." - removed
Output schema / $defs / PolicyMismatch / properties / policy / titleRemoved value: -"Policy" - changed
Output schema / $defs / PolicyMismatch / properties / requested / descriptionPrevious value: -"What the reader requests, human-readable: `\"RELIABLE\"`, `\"100 ms\"`, `\"infinite\"`."New value: +"What the reader requests, e.g. `\"RELIABLE\"`, `\"100 ms\"`." - removed
Output schema / $defs / PolicyMismatch / properties / requested / titleRemoved value: -"Requested" - removed
Output schema / $defs / PolicyMismatch / properties / rule / titleRemoved value: -"Rule" - removed
Output schema / $defs / PolicyMismatch / titleRemoved value: -"PolicyMismatch" - added
Output schema / $defs / SuggestedFixAdded value: +{ + "additionalProperties": false, + "properties": { + "kind": { + "description": "Form of `text`: a CLI command, a ROS 2 QoS override, a DDS profile or a launch parameter.", + "enum": [ + "cli", + "qos_override", + "dds_profile", + "launch_param" + ], + "type": "string" + }, + "target": { + "description": "Which node or side to change, writer or reader.", + "type": "string" + }, + "text": { + "description": "The exact command, YAML or XML to apply.", + "type": "string" + }, + "tradeoff": { + "description": "What is lost by applying it.", + "type": "string" + }, + "verify_with": { + "description": "The TopicForge tool to run again to confirm the repair.", + "type": "string" + } + }, + "required": [ + "kind", + "target", + "text", + "tradeoff", + "verify_with" + ], + "type": "object" +} - changed
Output schema / properties / hints / descriptionPrevious value: -"Leads that are not findings: orphan topics with near-identical names (typos), type id differences, pairs that could not be fully checked."New value: +"Leads that are not findings: probable typos, type id notes, incomplete checks." - removed
Output schema / properties / hints / titleRemoved value: -"Hints" - changed
Output schema / properties / matched / descriptionPrevious value: -"Pairs that will be matched by DDS given the announced QoS: same topic and type name, overlapping partitions, no incompatible RxO policy (a pair with only a `risky` History finding still counts). Actual data flow is not observed."New value: +"Pairs DDS will connect given the announced QoS (data flow is not observed)." - removed
Output schema / properties / matched / titleRemoved value: -"Matched" - removed
Output schema / properties / matched_total / titleRemoved value: -"Matched Total" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - changed
Output schema / properties / not_matched / descriptionPrevious value: -"Pairs separated by partition or type name. No data flows between them. An empty `reports` with a non-empty `not_matched` does not mean the bus is healthy."New value: +"Pairs DDS never matches (partition or type name). Non-empty with empty `reports` is NOT all-clear: no data flows between them." - removed
Output schema / properties / not_matched / titleRemoved value: -"Not Matched" - removed
Output schema / properties / not_matched_total / titleRemoved value: -"Not Matched Total" - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - changed
Output schema / properties / pairs_checked / descriptionPrevious value: -"Same-topic (reader, writer) pairs examined, including those reported in `not_matched`."New value: +"Same-topic (reader, writer) pairs examined." - removed
Output schema / properties / pairs_checked / titleRemoved value: -"Pairs Checked" - removed
Output schema / properties / policies_checked / titleRemoved value: -"Policies Checked" - changed
Output schema / properties / policies_unchecked / descriptionPrevious value: -"Policies and facts this scan does not cover, each with a one-line reason. A clean result says nothing about them."New value: +"What the scan does not cover, each with a reason; a clean result says nothing about them." - removed
Output schema / properties / policies_unchecked / titleRemoved value: -"Policies Unchecked" - changed
Output schema / properties / reports / descriptionPrevious value: -"Pairs that share a partition and a type but have incompatible or risky QoS."New value: +"Pairs sharing partition and type with incompatible or risky QoS." - removed
Output schema / properties / reports / titleRemoved value: -"Reports" - removed
Output schema / properties / reports_total / titleRemoved value: -"Reports Total" - added
Output schema / properties / summaryAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One-line reading of the scan: counts of incompatible, not matched and matched pairs." +} - removed
Output schema / properties / topics_scanned / titleRemoved value: -"Topics Scanned" - changed
Output schema / properties / truncated / descriptionPrevious value: -"True when `reports`, `matched` or `not_matched` was cut to its cap (200 entries each, incompatible reports first): see the `*_total` fields. Narrow the scan with `topic`."New value: +"True when `reports`, `matched` or `not_matched` hit the 200-entry cap (see `*_total`); narrow with `topic`." - removed
Output schema / properties / truncated / titleRemoved value: -"Truncated"
- Added
get_node_info - Changed
get_topic_info28 fields changed- changed
Input schema / properties / topic / descriptionPrevious value: -"Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and underscores; everything else (whitespace, quotes, shell metacharacters, `//`, trailing `/`) is rejected before reaching the `ros2` CLI."New value: +"Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - removed
Input schema / titleRemoved value: -"get_topic_infoArguments" - removed
Output schema / $defs / QosProfileRemoved value: -{ - "additionalProperties": false, - "description": "QoS profile of one DDS endpoint (reader or writer).\n\nCovers the policies behind most \"subscriber receives nothing\" cases.\nVendor-specific extensions are ignored.", - "properties": { - "data_representation": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Data representations the endpoint accepts or offers, from `XCDR1` and `XCDR2`. `None` when not announced.", - "title": "Data Representation" - }, - "deadline_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Deadline QoS in nanoseconds. `None` means no deadline (infinite, the DDS default). A reader deadline tighter (smaller) than a writer deadline is incompatible: the writer cannot guarantee the reader's promise.", - "title": "Deadline Ns" - }, - "destination_order": { - "anyOf": [ - { - "enum": [ - "BY_RECEPTION_TIMESTAMP", - "BY_SOURCE_TIMESTAMP" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DestinationOrder QoS. `None` when not announced.", - "title": "Destination Order" - }, - "durability": { - "description": "DDS Durability QoS. `VOLATILE` writers do not retain samples for late joiners; `TRANSIENT_LOCAL` writers do. A `TRANSIENT_LOCAL` reader cannot match a `VOLATILE` writer.", - "enum": [ - "VOLATILE", - "TRANSIENT_LOCAL", - "TRANSIENT", - "PERSISTENT" - ], - "title": "Durability", - "type": "string" - }, - "history": { - "anyOf": [ - { - "enum": [ - "KEEP_LAST", - "KEEP_ALL" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DDS History QoS. `KEEP_LAST` keeps a bounded ring buffer of size `history_depth`; `KEEP_ALL` keeps every sample (memory permitting). Mixed `KEEP_ALL` reader with `KEEP_LAST` writer is risky but not strictly incompatible. `None` when it is not known: DDS discovery does not carry History, so it is reported only for TopicForge's own endpoints and for Cyclone DDS peers that set something other than the default (see `history_note`).", - "title": "History" - }, - "history_depth": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Depth for `KEEP_LAST`. `None` for `KEEP_ALL` or when `history` is `None`.", - "title": "History Depth" - }, - "history_note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Set when `history` was left out because discovery did not announce it.", - "title": "History Note" - }, - "latency_budget_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "LatencyBudget QoS in nanoseconds (a delivery-delay hint). `0` is the DDS default. `None` means infinite or not announced.", - "title": "Latency Budget Ns" - }, - "liveliness_kind": { - "anyOf": [ - { - "enum": [ - "AUTOMATIC", - "MANUAL_BY_PARTICIPANT", - "MANUAL_BY_TOPIC" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Liveliness QoS kind. `AUTOMATIC` is asserted by the middleware; the two `MANUAL_*` kinds need the application to write or assert liveliness, and the endpoint is declared not alive when it does not within `liveliness_lease_ns`. `None` when not announced.", - "title": "Liveliness Kind" - }, - "liveliness_lease_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Liveliness lease duration in nanoseconds. `None` means infinite (the DDS default) or not announced.", - "title": "Liveliness Lease Ns" - }, - "ownership_kind": { - "anyOf": [ - { - "enum": [ - "SHARED", - "EXCLUSIVE" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Ownership QoS kind. With `EXCLUSIVE`, only the writer with the highest `ownership_strength` delivers samples to a reader. `None` when not announced.", - "title": "Ownership Kind" - }, - "ownership_strength": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Ownership strength of a writer (`EXCLUSIVE` ownership). `None` when not announced.", - "title": "Ownership Strength" - }, - "partitions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Partition names the endpoint's Publisher or Subscriber belongs to. No Partition policy is the default partition, reported as `[\"\"]` (never an empty list). Endpoints only match when their partitions intersect. `None` only when the backend cannot read partitions at all (unknown, not the default).", - "title": "Partitions" - }, - "reliability": { - "description": "DDS Reliability QoS. `RELIABLE` retries lost samples; `BEST_EFFORT` does not. A `RELIABLE` reader cannot match a `BEST_EFFORT` writer.", - "enum": [ - "RELIABLE", - "BEST_EFFORT" - ], - "title": "Reliability", - "type": "string" - }, - "unknown_policies": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Policies the endpoint announced but whose value could not be read (for example `Deadline`). Their duration fields are `None` here, which does NOT mean infinite for these: the scan does not compare them.", - "title": "Unknown Policies" - } - }, - "required": [ - "reliability", - "durability" - ], - "title": "QosProfile", - "type": "object" -} - added
Output schema / $defs / SideQosAdded value: +{ + "additionalProperties": false, + "properties": { + "durability": { + "description": "Shared durability of this side, or `mixed`; `transient_local` marks a latched topic such as `/tf_static`.", + "enum": [ + "volatile", + "transient_local", + "mixed" + ], + "type": "string" + }, + "endpoint_count": { + "description": "Endpoints of this side the values were read from.", + "minimum": 1, + "type": "integer" + }, + "reliability": { + "description": "Reliability shared by the endpoints of this side, or `mixed` when they disagree.", + "enum": [ + "reliable", + "best_effort", + "mixed" + ], + "type": "string" + } + }, + "required": [ + "reliability", + "durability", + "endpoint_count" + ], + "type": "object" +} - changed
Output schema / descriptionPrevious value: -"Description of a single ROS2 topic.\n\nCarries optional DDS-side enrichment fields when the active middleware\nbackend can resolve them (CycloneDDS / Fast DDS). The ROS2 CLI adapter and\nthe mock ROS2 path leave them `None`."New value: +"Detail of a single ROS2 topic: counts, QoS per side and the nodes on each side." - removed
Output schema / properties / message_type / descriptionRemoved value: -"ROS2 message type, e.g. `geometry_msgs/msg/Twist`." - removed
Output schema / properties / message_type / titleRemoved value: -"Message Type" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - removed
Output schema / properties / name / descriptionRemoved value: -"Fully qualified topic name, e.g. `/cmd_vel`." - removed
Output schema / properties / name / titleRemoved value: -"Name" - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - removed
Output schema / properties / publisher_count / descriptionRemoved value: -"Publishers known to the graph." - removed
Output schema / properties / publisher_count / titleRemoved value: -"Publisher Count" - added
Output schema / properties / publisher_nodesAdded value: +{ + "description": "Publisher node names (`ros2 topic info --verbose`); empty when unknown.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / publisher_qosAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/SideQos" + }, + { + "type": "null" + } + ], + "default": null, + "description": "QoS of the publishers. `None` when it was not read: see `publisher_qos_note`." +} - added
Output schema / properties / publisher_qos_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `publisher_qos` is `None`. `None` when it is set." +} - removed
Output schema / properties / qos_durabilityRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Durability announced by the topic's publishers: `volatile`, `transient_local` (late subscribers receive the last samples; typical of latched topics such as `/tf_static`), or `mixed` when publishers disagree. `null` when unknown, with the same rules as `qos_reliability`.", - "title": "Qos Durability" -} - removed
Output schema / properties / qos_profileRemoved value: -{ - "anyOf": [ - { - "$ref": "#/$defs/QosProfile" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Effective DDS QoS profile for this topic when resolvable. `None` from the ROS2 CLI adapter or when the DDS module is inactive. The DDS module populates this on a best-effort basis (picks one representative endpoint if reader/writer QoS differ)." -} - removed
Output schema / properties / qos_reliabilityRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Reliability announced by the topic's publishers: `reliable`, `best_effort`, or `mixed` when publishers disagree. `null` when unknown: the topic has no publisher, or the value was not read (`list_topics` does not read QoS; `get_topic_info` does).", - "title": "Qos Reliability" -} - removed
Output schema / properties / reader_countRemoved value: -{ - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DDS reader-endpoint count when the active backend can resolve endpoint-level info (Cyclone / Fast DDS). `None` from the ROS2 CLI adapter or when the DDS module is inactive.", - "title": "Reader Count" -} - removed
Output schema / properties / subscriber_count / descriptionRemoved value: -"Subscribers known to the graph." - removed
Output schema / properties / subscriber_count / titleRemoved value: -"Subscriber Count" - added
Output schema / properties / subscriber_nodesAdded value: +{ + "description": "Subscriber node names; empty when unknown.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / subscription_qosAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/SideQos" + }, + { + "type": "null" + } + ], + "default": null, + "description": "QoS of the subscriptions. `None` when it was not read: see `subscription_qos_note`." +} - added
Output schema / properties / subscription_qos_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `subscription_qos` is `None`. `None` when it is set." +} - removed
Output schema / properties / writer_countRemoved value: -{ - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DDS writer-endpoint count when the active backend can resolve endpoint-level info. `None` from the ROS2 CLI adapter or when the DDS module is inactive.", - "title": "Writer Count" -}
- Changed
health_check50 fields changed- removed
Input schema / titleRemoved value: -"health_checkArguments" - added
Output schema / properties / contract_versionAdded value: +{ + "default": 2, + "description": "Output contract version; changes only when a field is renamed, removed or retyped.", + "minimum": 1, + "type": "integer" +} - changed
Output schema / properties / dds_backend / descriptionPrevious value: -"DDS backend of the adapter actually serving requests. `none` when the DDS module is not active (default for ROS2-only installs). `mock` for synthetic fixtures. `cyclone` requires `pip install \"topicforge[dds-cyclone]\"` (Eclipse CycloneDDS); `fast` requires a Fast DDS Python binding built from eProsima sources (not on PyPI); `opendds` and `dust` are permanent stub adapters that never serve."New value: +"DDS backend serving: `mock`, `cyclone`, `fast`, or `none` (DDS module inactive)." - changed
Output schema / properties / dds_backend / enumPrevious value: -[ - "mock", - "cyclone", - "fast", - "opendds", - "dust", - "none" -]New value: +[ + "mock", + "cyclone", + "fast", + "none" +] - removed
Output schema / properties / dds_backend / titleRemoved value: -"Dds Backend" - removed
Output schema / properties / dds_domain_id / titleRemoved value: -"Dds Domain Id" - added
Output schema / properties / dds_inactive_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `dds_backend` is `none` while ROS 2 serves (not selected, binding missing, or failed to start); else `null`." +} - removed
Output schema / properties / dds_inactive_reasonRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Why `dds_backend` is `none` while the ROS 2 CLI serves: the backend was not selected (`TOPICFORGE_DDS_BACKEND` unset or `mock`), its Python binding is not installed, or the binding is installed but the adapter failed to start. `null` when a DDS backend is serving or the cause is not known.", - "title": "Dds Inactive Reason" -} - changed
Output schema / properties / dds_security / descriptionPrevious value: -"DDS Security is not handled. On a secured domain TopicForge can show participants but not protected endpoints or data."New value: +"DDS Security is not handled: protected endpoints and data are invisible." - removed
Output schema / properties / dds_security / titleRemoved value: -"Dds Security" - changed
Output schema / properties / max_sample_count / descriptionPrevious value: -"Server-side cap on the number of samples returned per `sample_messages` call. Requests above this limit are silently clamped; the value is exposed here so a client can size its requests proactively. Constant within a given server version."New value: +"Server cap on samples per call; larger requests are clamped." - removed
Output schema / properties / max_sample_count / titleRemoved value: -"Max Sample Count" - changed
Output schema / properties / middleware_available / descriptionPrevious value: -"True when a DDS backend is serving (`dds_backend` is not `none`). When the DDS module is inactive (`dds_backend == 'none'`), whether the *configured* backend's Python bindings are importable, so a missing binding is visible."New value: +"True when a DDS backend serves; else whether the configured binding is importable." - removed
Output schema / properties / middleware_available / titleRemoved value: -"Middleware Available" - changed
Output schema / properties / mode / descriptionPrevious value: -"Runtime mode of the adapter actually serving requests: `mock` or `live`. `live` with `ros_backend` `none` means the DDS tools are live and the ROS 2 tools are not available. Can differ from `requested_mode` when a live backend could not start (e.g. `live` requested without `ros2` installed falls back to `mock`)."New value: +"Mode actually serving: `mock` or `live` (`live` with `ros_backend` `none`: DDS tools only). May differ from `requested_mode` after a fallback." - removed
Output schema / properties / mode / titleRemoved value: -"Mode" - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - removed
Output schema / properties / now_ns / titleRemoved value: -"Now Ns" - changed
Output schema / properties / observed_domain_note / descriptionPrevious value: -"Plain statement of which DDS domain is observed, set when a DDS module is active: only the domain joined at startup is visible, a program on another domain is invisible."New value: +"Which DDS domain is observed: only the one joined at startup; others are invisible." - removed
Output schema / properties / observed_domain_note / titleRemoved value: -"Observed Domain Note" - changed
Output schema / properties / observer_started_ns / descriptionPrevious value: -"Wall-clock time (ns since epoch) when the DDS observer joined the bus. Nothing earlier than this was watched: `now_ns` minus this is how long TopicForge has been observing. `None` without a live DDS observer."New value: +"ns when the DDS observer joined; nothing earlier was watched. `None` without one." - removed
Output schema / properties / observer_started_ns / titleRemoved value: -"Observer Started Ns" - changed
Output schema / properties / payload_decoding / descriptionPrevious value: -"Whether DDS user-topic payloads are decoded. `disabled` today: `peek_dds_samples` and `topic_metrics` do not return message content for user topics."New value: +"`disabled`: DDS user-topic payloads are not decoded." - removed
Output schema / properties / payload_decoding / titleRemoved value: -"Payload Decoding" - added
Output schema / properties / payload_decoding_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "user-topic payload decoding is switched off until it is validated on a real bus; builtin discovery topics are still readable", + "description": "Reason for `payload_decoding`." +} - removed
Output schema / properties / payload_decoding_reasonRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": "user-topic payload decoding is switched off until it is validated on a real bus; builtin discovery topics are still readable", - "description": "One-line reason for `payload_decoding`.", - "title": "Payload Decoding Reason" -} - changed
Output schema / properties / requested_mode / descriptionPrevious value: -"Mode requested via configuration (may be `auto`)."New value: +"Mode requested by configuration (may be `auto`)." - removed
Output schema / properties / requested_mode / titleRemoved value: -"Requested Mode" - added
Output schema / properties / rmw_implementationAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "ROS 2 middleware, e.g. `rmw_fastrtps_cpp`; `None` when `rmw_source` is `none`." +} - added
Output schema / properties / rmw_sourceAdded value: +{ + "default": "none", + "description": "`env`, `ros2_cli`, `distro_default` (not verified on the graph) or `none`.", + "enum": [ + "env", + "ros2_cli", + "distro_default", + "none" + ], + "type": "string" +} - removed
Output schema / properties / ros2_available / titleRemoved value: -"Ros2 Available" - changed
Output schema / properties / ros2_distro / descriptionPrevious value: -"Value of `ROS_DISTRO` if set in the environment. **Env disclosure, by design**: under the local-trust threat model (see README 'Security model'), the MCP client is a trusted agent on a machine the user controls, and exposing the ROS2 distro lets it adapt to e.g. `humble`/`jazzy` differences. For a hosted multi-tenant TopicForge endpoint this field would be scrubbed ."New value: +"`ROS_DISTRO` if set (disclosed by design under the local-trust model)." - removed
Output schema / properties / ros2_distro / titleRemoved value: -"Ros2 Distro" - changed
Output schema / properties / ros_backend / descriptionPrevious value: -"Active ROS2 backend. `ros2_cli` when the `ros2` CLI is on PATH and live mode resolves to a Ros2CliAdapter (alone or as the ROS half of a composite). `mock` when MockAdapter serves the ROS surface. `none` when no ROS2 backend is active (e.g. DDS-only live install with no `ros2` CLI). Together with `dds_backend` it tells the ROS2 and DDS halves of the runtime apart."New value: +"ROS 2 backend: `ros2_cli`, `mock` or `none`." - removed
Output schema / properties / ros_backend / titleRemoved value: -"Ros Backend" - changed
Output schema / properties / ros_tools_available / descriptionPrevious value: -"True when the ROS 2 tools (`list_topics`, `get_topic_info`, `sample_messages`, `analyze_bag`, `peek_bag_samples`) can run, i.e. `ros_backend` is not `none`. False on a DDS-only setup: use `list_endpoints` for topics and wiring there."New value: +"True when the ROS 2 tools can run (`ros_backend` is not `none`); on a DDS-only setup use `list_endpoints`." - removed
Output schema / properties / ros_tools_available / titleRemoved value: -"Ros Tools Available" - changed
Output schema / properties / server_version / descriptionPrevious value: -"TopicForge server version (matches the PyPI release of the `topicforge` package)."New value: +"TopicForge version." - removed
Output schema / properties / server_version / titleRemoved value: -"Server Version" - changed
Output schema / properties / sim_clock_published / descriptionPrevious value: -"Whether `/clock` has at least one publisher on the ROS 2 graph. True suggests nodes may run on simulated time (`use_sim_time`), so `header.stamp` values are sim time, not wall time (compare with `received_ns`). It is a hint: a publisher on `/clock` does not prove a given node follows it. `None` in mock mode or when the `ros2` CLI could not answer."New value: +"Whether `/clock` had a publisher in the last graph read (a hint that stamps may be sim time). `None` in mock or without a recent read; `health_check` never runs `ros2`." - removed
Output schema / properties / sim_clock_published / titleRemoved value: -"Sim Clock Published" - changed
Output schema / properties / tracker_cache_evictions / descriptionPrevious value: -"Discovery entries dropped because a tracker cache was full (4096 per cache). Non-zero means the bus is bigger than what is listed."New value: +"Discovery entries dropped from a full tracker cache (4096 each); non-zero means the bus is bigger than listed." - removed
Output schema / properties / tracker_cache_evictions / titleRemoved value: -"Tracker Cache Evictions" - changed
Output schema / properties / tracker_errors / descriptionPrevious value: -"Discovery tracker passes that raised (swallowed and logged). Non-zero means gaps."New value: +"Tracker passes that raised; non-zero means gaps." - removed
Output schema / properties / tracker_errors / titleRemoved value: -"Tracker Errors" - changed
Output schema / properties / tracker_last_pass_ns / descriptionPrevious value: -"Wall-clock time (ns since epoch) of the last completed tracker pass. A value far older than `now_ns` means lifecycle is stale."New value: +"ns of the last tracker pass; far older than `now_ns` means stale lifecycle." - removed
Output schema / properties / tracker_last_pass_ns / titleRemoved value: -"Tracker Last Pass Ns" - removed
Output schema / properties / tracker_passes / titleRemoved value: -"Tracker Passes" - changed
Output schema / properties / tracker_running / descriptionPrevious value: -"Whether the continuous discovery tracker thread is alive (Cyclone). `None` when the backend has no tracker."New value: +"Whether the discovery tracker thread is alive (Cyclone); `None` without a tracker." - removed
Output schema / properties / tracker_running / titleRemoved value: -"Tracker Running"
- Changed
list_endpoints125 fields changed- changed
Input schema / properties / domain_id / descriptionPrevious value: -"Accepted for compatibility (0..232). TopicForge observes the domain it joined at startup (TOPICFORGE_DDS_DOMAIN_ID); this argument does not switch domains, and the response `domain_id` says which one was observed."New value: +"Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value." - removed
Input schema / properties / domain_id / titleRemoved value: -"Domain Id" - added
Input schema / properties / fieldsAdded value: +{ + "default": "full", + "description": "`full` (default) or `compact`: one-line `qos_summary` instead of the full `qos`, no `type_id`, `announced_ns` or `ros_topic_note`. Use `compact` on large buses.", + "enum": [ + "full", + "compact" + ], + "type": "string" +} - changed
Input schema / properties / include_departed / descriptionPrevious value: -"Also list endpoints whose participant left the bus (flagged with `gone_ns`). Defaults to false; `by_topic` reports them as `departed_writers` / `departed_readers` either way."New value: +"Also list endpoints whose participant left (flagged with `gone_ns`); `by_topic` reports them either way. Default false." - removed
Input schema / properties / include_departed / titleRemoved value: -"Include Departed" - added
Input schema / properties / include_internalAdded value: +{ + "default": false, + "description": "Also list ROS 2 service and action endpoints (`rq/`, `rr/`, `rs/`, `rp/`, `ra/`, `ros_discovery_info`), most of a small robot's endpoints. Default false.", + "type": "boolean" +} - changed
Input schema / properties / include_observer / descriptionPrevious value: -"Include TopicForge's own observer participant's endpoints. Defaults to false."New value: +"Include TopicForge's own observer endpoints. Default false." - removed
Input schema / properties / include_observer / titleRemoved value: -"Include Observer" - changed
Input schema / properties / participant_guid / descriptionPrevious value: -"Only endpoints owned by this participant, as the guid reported by `list_participants` or by an earlier `list_endpoints`. Omit for all participants."New value: +"Only endpoints of this participant (a guid from `list_participants`). Omit for all." - removed
Input schema / properties / participant_guid / titleRemoved value: -"Participant Guid" - changed
Input schema / properties / topic / descriptionPrevious value: -"Only endpoints on this DDS topic name: a bare name such as `scan` or a ROS 2 mangled name such as `rt/scan` (exact name first, then the alternate form). Omit to list every topic."New value: +"Only endpoints on this DDS topic: `scan` or `rt/scan` (exact name first, then the alternate form). Omit for every topic." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - removed
Input schema / titleRemoved value: -"list_endpointsArguments" - removed
Output schema / $defs / DepartedEndpoint / descriptionRemoved value: -"An endpoint whose participant left the bus (crash, clean exit or lease expiry)." - removed
Output schema / $defs / DepartedEndpoint / properties / gone_ns / titleRemoved value: -"Gone Ns" - removed
Output schema / $defs / DepartedEndpoint / properties / guid / descriptionRemoved value: -"GUID of the departed endpoint." - removed
Output schema / $defs / DepartedEndpoint / properties / guid / titleRemoved value: -"Guid" - removed
Output schema / $defs / DepartedEndpoint / properties / participant_guid / descriptionRemoved value: -"GUID of the participant that owned it." - removed
Output schema / $defs / DepartedEndpoint / properties / participant_guid / titleRemoved value: -"Participant Guid" - removed
Output schema / $defs / DepartedEndpoint / properties / participant_name / descriptionRemoved value: -"Announced name of that participant, `None` when it set none." - removed
Output schema / $defs / DepartedEndpoint / properties / participant_name / titleRemoved value: -"Participant Name" - removed
Output schema / $defs / DepartedEndpoint / titleRemoved value: -"DepartedEndpoint" - removed
Output schema / $defs / EndpointInfo / descriptionRemoved value: -"One DDS endpoint (writer or reader) announced through discovery." - removed
Output schema / $defs / EndpointInfo / properties / activityRemoved value: -{ - "default": null, - "description": "Reserved for a future liveness signal. Always `None` today.", - "title": "Activity", - "type": "null" -} - removed
Output schema / $defs / EndpointInfo / properties / activity_noteRemoved value: -{ - "default": "not observed: TopicForge holds no reader on user topics, so it cannot tell a silent or hung writer from a healthy one", - "description": "Why `activity` is not populated.", - "title": "Activity Note", - "type": "string" -} - changed
Output schema / $defs / EndpointInfo / properties / announced_ns / descriptionPrevious value: -"Source timestamp of the discovery announcement, ns since epoch, read on the announcing side's clock (it can differ from this host's clock). `None` when not available."New value: +"Source timestamp of the announcement, on the announcer's clock; `None` if unavailable." - removed
Output schema / $defs / EndpointInfo / properties / announced_ns / titleRemoved value: -"Announced Ns" - added
Output schema / $defs / EndpointInfo / properties / dds_topicAdded value: +{ + "type": "string" +} - removed
Output schema / $defs / EndpointInfo / properties / domain_idRemoved value: -{ - "description": "DDS domain the endpoint was observed on.", - "maximum": 232, - "minimum": 0, - "title": "Domain Id", - "type": "integer" -} - changed
Output schema / $defs / EndpointInfo / properties / gone_ns / descriptionPrevious value: -"`None` for a live endpoint. For a departed endpoint (only listed with `include_departed`): when its participant was lost (the participant's `lost_ns`, an upper bound of the death)."New value: +"`None` for a live endpoint; for a departed one (`include_departed`) when its participant was lost (upper bound)." - removed
Output schema / $defs / EndpointInfo / properties / gone_ns / titleRemoved value: -"Gone Ns" - removed
Output schema / $defs / EndpointInfo / properties / guid / descriptionRemoved value: -"GUID of the endpoint, `xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx`." - removed
Output schema / $defs / EndpointInfo / properties / guid / titleRemoved value: -"Guid" - removed
Output schema / $defs / EndpointInfo / properties / is_observer / titleRemoved value: -"Is Observer" - removed
Output schema / $defs / EndpointInfo / properties / mode_effectiveRemoved value: -{ - "description": "Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`.", - "enum": [ - "mock", - "live" - ], - "title": "Mode Effective", - "type": "string" -} - removed
Output schema / $defs / EndpointInfo / properties / participant_guid / descriptionRemoved value: -"GUID of the owning participant, same format as `list_participants`." - removed
Output schema / $defs / EndpointInfo / properties / participant_guid / titleRemoved value: -"Participant Guid" - removed
Output schema / $defs / EndpointInfo / properties / participant_name / descriptionRemoved value: -"Announced name of the owning participant, `None` when it set none." - removed
Output schema / $defs / EndpointInfo / properties / participant_name / titleRemoved value: -"Participant Name" - removed
Output schema / $defs / EndpointInfo / properties / participant_vendor / descriptionRemoved value: -"Vendor of the owning participant, same value as `list_participants` (`unknown` when it cannot be determined)." - removed
Output schema / $defs / EndpointInfo / properties / participant_vendor / titleRemoved value: -"Participant Vendor" - changed
Output schema / $defs / EndpointInfo / properties / qos / descriptionPrevious value: -"QoS the endpoint announced. Durations (`deadline_ns`, `liveliness_lease_ns`, `latency_budget_ns`) of `None` mean infinite or not set. `None` when reliability or durability could not be resolved."New value: +"Announced QoS; `None` when reliability or durability could not be resolved. A duration of `None` is infinite or unset." - added
Output schema / $defs / EndpointInfo / properties / qos_summaryAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One-line QoS, set only with `fields` `compact` (where `qos` is `None`)." +} - removed
Output schema / $defs / EndpointInfo / properties / role / titleRemoved value: -"Role" - added
Output schema / $defs / EndpointInfo / properties / ros_topicAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "ROS 2 name of the topic, for example `/scan`. `None` when the endpoint is not a ROS 2 topic: see `ros_topic_note`." +} - added
Output schema / $defs / EndpointInfo / properties / ros_topic_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `ros_topic` is `None`. `None` when it is set." +} - removed
Output schema / $defs / EndpointInfo / properties / topicRemoved value: -{ - "description": "DDS topic name.", - "title": "Topic", - "type": "string" -} - removed
Output schema / $defs / EndpointInfo / properties / type_id / titleRemoved value: -"Type Id" - removed
Output schema / $defs / EndpointInfo / properties / type_name / descriptionRemoved value: -"Announced data type name." - removed
Output schema / $defs / EndpointInfo / properties / type_name / titleRemoved value: -"Type Name" - changed
Output schema / $defs / EndpointInfo / requiredPrevious value: -[ - "guid", - "role", - "participant_guid", - "topic", - "is_observer", - "domain_id", - "mode_effective" -]New value: +[ + "guid", + "role", + "participant_guid", + "dds_topic", + "is_observer" +] - removed
Output schema / $defs / EndpointInfo / titleRemoved value: -"EndpointInfo" - removed
Output schema / $defs / QosProfile / descriptionRemoved value: -"QoS profile of one DDS endpoint (reader or writer).\n\nCovers the policies behind most \"subscriber receives nothing\" cases.\nVendor-specific extensions are ignored." - changed
Output schema / $defs / QosProfile / properties / data_representation / descriptionPrevious value: -"Data representations the endpoint accepts or offers, from `XCDR1` and `XCDR2`. `None` when not announced."New value: +"`XCDR1` / `XCDR2` accepted or offered; `None` when not announced." - removed
Output schema / $defs / QosProfile / properties / data_representation / titleRemoved value: -"Data Representation" - changed
Output schema / $defs / QosProfile / properties / deadline_ns / descriptionPrevious value: -"Deadline QoS in nanoseconds. `None` means no deadline (infinite, the DDS default). A reader deadline tighter (smaller) than a writer deadline is incompatible: the writer cannot guarantee the reader's promise."New value: +"Deadline in ns; `None` is infinite. A reader deadline shorter than the writer's is incompatible." - removed
Output schema / $defs / QosProfile / properties / deadline_ns / titleRemoved value: -"Deadline Ns" - removed
Output schema / $defs / QosProfile / properties / destination_order / titleRemoved value: -"Destination Order" - changed
Output schema / $defs / QosProfile / properties / durability / descriptionPrevious value: -"DDS Durability QoS. `VOLATILE` writers do not retain samples for late joiners; `TRANSIENT_LOCAL` writers do. A `TRANSIENT_LOCAL` reader cannot match a `VOLATILE` writer."New value: +"`VOLATILE` keeps nothing for late joiners, `TRANSIENT_LOCAL` does; a TRANSIENT_LOCAL reader cannot match a VOLATILE writer." - removed
Output schema / $defs / QosProfile / properties / durability / titleRemoved value: -"Durability" - changed
Output schema / $defs / QosProfile / properties / history / descriptionPrevious value: -"DDS History QoS. `KEEP_LAST` keeps a bounded ring buffer of size `history_depth`; `KEEP_ALL` keeps every sample (memory permitting). Mixed `KEEP_ALL` reader with `KEEP_LAST` writer is risky but not strictly incompatible. `None` when it is not known: DDS discovery does not carry History, so it is reported only for TopicForge's own endpoints and for Cyclone DDS peers that set something other than the default (see `history_note`)."New value: +"`KEEP_LAST` (ring of `history_depth`) or `KEEP_ALL`; `None` when discovery did not announce it (see `history_note`)." - removed
Output schema / $defs / QosProfile / properties / history / titleRemoved value: -"History" - changed
Output schema / $defs / QosProfile / properties / history_depth / descriptionPrevious value: -"Depth for `KEEP_LAST`. `None` for `KEEP_ALL` or when `history` is `None`."New value: +"Depth for `KEEP_LAST`; else `None`." - removed
Output schema / $defs / QosProfile / properties / history_depth / titleRemoved value: -"History Depth" - changed
Output schema / $defs / QosProfile / properties / history_note / descriptionPrevious value: -"Set when `history` was left out because discovery did not announce it."New value: +"Why `history` is `None`." - removed
Output schema / $defs / QosProfile / properties / history_note / titleRemoved value: -"History Note" - changed
Output schema / $defs / QosProfile / properties / latency_budget_ns / descriptionPrevious value: -"LatencyBudget QoS in nanoseconds (a delivery-delay hint). `0` is the DDS default. `None` means infinite or not announced."New value: +"LatencyBudget in ns (a hint); `0` is the default, `None` is infinite or not announced." - removed
Output schema / $defs / QosProfile / properties / latency_budget_ns / titleRemoved value: -"Latency Budget Ns" - changed
Output schema / $defs / QosProfile / properties / liveliness_kind / descriptionPrevious value: -"Liveliness QoS kind. `AUTOMATIC` is asserted by the middleware; the two `MANUAL_*` kinds need the application to write or assert liveliness, and the endpoint is declared not alive when it does not within `liveliness_lease_ns`. `None` when not announced."New value: +"`AUTOMATIC`, or `MANUAL_*` (the app must assert liveliness within `liveliness_lease_ns`); `None` when not announced." - removed
Output schema / $defs / QosProfile / properties / liveliness_kind / titleRemoved value: -"Liveliness Kind" - changed
Output schema / $defs / QosProfile / properties / liveliness_lease_ns / descriptionPrevious value: -"Liveliness lease duration in nanoseconds. `None` means infinite (the DDS default) or not announced."New value: +"Liveliness lease in ns; `None` is infinite or not announced." - removed
Output schema / $defs / QosProfile / properties / liveliness_lease_ns / titleRemoved value: -"Liveliness Lease Ns" - changed
Output schema / $defs / QosProfile / properties / ownership_kind / descriptionPrevious value: -"Ownership QoS kind. With `EXCLUSIVE`, only the writer with the highest `ownership_strength` delivers samples to a reader. `None` when not announced."New value: +"With `EXCLUSIVE` only the writer with the highest `ownership_strength` delivers; `None` when not announced." - removed
Output schema / $defs / QosProfile / properties / ownership_kind / titleRemoved value: -"Ownership Kind" - changed
Output schema / $defs / QosProfile / properties / ownership_strength / descriptionPrevious value: -"Ownership strength of a writer (`EXCLUSIVE` ownership). `None` when not announced."New value: +"Writer strength under `EXCLUSIVE` ownership; `None` when not announced." - removed
Output schema / $defs / QosProfile / properties / ownership_strength / titleRemoved value: -"Ownership Strength" - changed
Output schema / $defs / QosProfile / properties / partitions / descriptionPrevious value: -"Partition names the endpoint's Publisher or Subscriber belongs to. No Partition policy is the default partition, reported as `[\"\"]` (never an empty list). Endpoints only match when their partitions intersect. `None` only when the backend cannot read partitions at all (unknown, not the default)."New value: +"Partition names; the default partition is `[\"\"]`, never `[]`. Endpoints match only if partitions intersect; `None` when unreadable." - removed
Output schema / $defs / QosProfile / properties / partitions / titleRemoved value: -"Partitions" - changed
Output schema / $defs / QosProfile / properties / reliability / descriptionPrevious value: -"DDS Reliability QoS. `RELIABLE` retries lost samples; `BEST_EFFORT` does not. A `RELIABLE` reader cannot match a `BEST_EFFORT` writer."New value: +"`RELIABLE` retries lost samples, `BEST_EFFORT` does not; a RELIABLE reader cannot match a BEST_EFFORT writer." - removed
Output schema / $defs / QosProfile / properties / reliability / titleRemoved value: -"Reliability" - changed
Output schema / $defs / QosProfile / properties / unknown_policies / descriptionPrevious value: -"Policies the endpoint announced but whose value could not be read (for example `Deadline`). Their duration fields are `None` here, which does NOT mean infinite for these: the scan does not compare them."New value: +"Policies announced but unreadable (e.g. `Deadline`): their fields are `None`, which does NOT mean infinite; the scan does not compare them." - removed
Output schema / $defs / QosProfile / properties / unknown_policies / titleRemoved value: -"Unknown Policies" - removed
Output schema / $defs / QosProfile / titleRemoved value: -"QosProfile" - removed
Output schema / $defs / TopicSummary / descriptionRemoved value: -"Per-topic roll-up of the listed endpoints, for spotting orphans." - added
Output schema / $defs / TopicSummary / properties / dds_topicAdded value: +{ + "type": "string" +} - changed
Output schema / $defs / TopicSummary / properties / departed_readers / descriptionPrevious value: -"Readers on this topic whose participant left, newest first."New value: +"Readers whose participant left, newest first." - removed
Output schema / $defs / TopicSummary / properties / departed_readers / titleRemoved value: -"Departed Readers" - changed
Output schema / $defs / TopicSummary / properties / departed_writers / descriptionPrevious value: -"Writers on this topic whose participant left, newest first (bounded memory: last 200 departed endpoints, 1 h). Explains a topic that lost its only writer."New value: +"Writers whose participant left, newest first (last 200, 1 h)." - removed
Output schema / $defs / TopicSummary / properties / departed_writers / titleRemoved value: -"Departed Writers" - changed
Output schema / $defs / TopicSummary / properties / orphan / descriptionPrevious value: -"`no_reader`: writers but no reader. `no_writer`: readers but no writer. `None` when both sides exist."New value: +"`no_reader` (writers only), `no_writer` (readers only), else `None`." - removed
Output schema / $defs / TopicSummary / properties / orphan / titleRemoved value: -"Orphan" - removed
Output schema / $defs / TopicSummary / properties / partitions / titleRemoved value: -"Partitions" - removed
Output schema / $defs / TopicSummary / properties / reader_count / descriptionRemoved value: -"Number of listed readers." - removed
Output schema / $defs / TopicSummary / properties / reader_count / titleRemoved value: -"Reader Count" - added
Output schema / $defs / TopicSummary / properties / ros_topicAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "ROS 2 name of the topic, for example `/scan`. `None` for a DDS topic that is not a ROS 2 topic." +} - removed
Output schema / $defs / TopicSummary / properties / topicRemoved value: -{ - "description": "DDS topic name.", - "title": "Topic", - "type": "string" -} - removed
Output schema / $defs / TopicSummary / properties / type_names / descriptionRemoved value: -"Distinct type names announced on this topic." - removed
Output schema / $defs / TopicSummary / properties / type_names / titleRemoved value: -"Type Names" - removed
Output schema / $defs / TopicSummary / properties / writer_count / descriptionRemoved value: -"Number of listed writers." - removed
Output schema / $defs / TopicSummary / properties / writer_count / titleRemoved value: -"Writer Count" - changed
Output schema / $defs / TopicSummary / requiredPrevious value: -[ - "topic", - "type_names", - "writer_count", - "reader_count", - "partitions" -]New value: +[ + "dds_topic", + "type_names", + "writer_count", + "reader_count", + "partitions" +] - removed
Output schema / $defs / TopicSummary / titleRemoved value: -"TopicSummary" - removed
Output schema / properties / by_topic / titleRemoved value: -"By Topic" - added
Output schema / properties / departed_endpoint_countAdded value: +{ + "default": 0, + "description": "Departed endpoints matching the filters; listed in `endpoints` only with `include_departed`.", + "minimum": 0, + "type": "integer" +} - removed
Output schema / properties / departed_endpointsRemoved value: -{ - "default": 0, - "description": "Departed endpoints (their participant left) matching the filters. They are in `endpoints` only with `include_departed`; `by_topic` always carries them as `departed_writers` / `departed_readers`.", - "minimum": 0, - "title": "Departed Endpoints", - "type": "integer" -} - removed
Output schema / properties / domain_id / descriptionRemoved value: -"DDS domain observed." - removed
Output schema / properties / domain_id / titleRemoved value: -"Domain Id" - removed
Output schema / properties / endpoints / titleRemoved value: -"Endpoints" - added
Output schema / properties / excluded_observer_endpoint_countAdded value: +{ + "default": 0, + "description": "TopicForge's own endpoints left out of `endpoints` (counted in `total`).", + "minimum": 0, + "type": "integer" +} - removed
Output schema / properties / excluded_observer_endpointsRemoved value: -{ - "default": 0, - "description": "Endpoints of TopicForge's own observer participant left out of `endpoints` (they are counted in `total_discovered`). Explains `total_discovered` vs `returned` together with the filters.", - "minimum": 0, - "title": "Excluded Observer Endpoints", - "type": "integer" -} - added
Output schema / properties / fieldsAdded value: +{ + "default": "full", + "description": "Detail level served: `compact` drops per-endpoint QoS detail, type id and notes.", + "enum": [ + "full", + "compact" + ], + "type": "string" +} - added
Output schema / properties / hidden_internal_endpoint_countAdded value: +{ + "default": 0, + "description": "Service/action endpoints (`rq/`, `rr/`, `rs/`, `rp/`, `ra/`, `ros_discovery_info`) left out while `include_internal` is false.", + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / hintsAdded value: +{ + "description": "Facts true of every endpoint, stated once (e.g. liveness is not observed).", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - changed
Output schema / properties / note / descriptionPrevious value: -"Hint when a `topic` filter matched nothing: names the closest known topics."New value: +"Set when a `topic` filter matched nothing: the closest known topics." - removed
Output schema / properties / note / titleRemoved value: -"Note" - removed
Output schema / properties / observer_guid / titleRemoved value: -"Observer Guid" - removed
Output schema / properties / returned / descriptionRemoved value: -"Length of `endpoints`." - removed
Output schema / properties / returned / titleRemoved value: -"Returned" - removed
Output schema / properties / snapshot_ns / titleRemoved value: -"Snapshot Ns" - added
Output schema / properties / totalAdded value: +{ + "description": "Endpoints in the discovery cache before any filter.", + "minimum": 0, + "type": "integer" +} - removed
Output schema / properties / total_discoveredRemoved value: -{ - "description": "Endpoints in the discovery cache before any filter.", - "minimum": 0, - "title": "Total Discovered", - "type": "integer" -} - removed
Output schema / properties / truncated / titleRemoved value: -"Truncated" - changed
Output schema / requiredPrevious value: -[ - "domain_id", - "snapshot_ns", - "endpoints", - "by_topic", - "total_discovered", - "returned", - "truncated", - "mode_effective" -]New value: +[ + "domain_id", + "snapshot_ns", + "endpoints", + "by_topic", + "total", + "returned", + "truncated", + "mode_effective" +]
- Added
list_nodes - Changed
list_participants52 fields changed- changed
Input schema / properties / domain_id / descriptionPrevious value: -"Accepted for compatibility (0..232). TopicForge observes the domain it joined at startup (TOPICFORGE_DDS_DOMAIN_ID); this argument does not switch domains, and the response `domain_id` says which one was observed."New value: +"Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value." - removed
Input schema / properties / domain_id / titleRemoved value: -"Domain Id" - removed
Input schema / titleRemoved value: -"list_participantsArguments" - added
Output schema / $defs / NameLifecycleAdded value: +{ + "additionalProperties": false, + "properties": { + "departures": { + "description": "Times a participant with this name was lost.", + "minimum": 0, + "type": "integer" + }, + "lives": { + "description": "Distinct GUIDs that carried this name; restarts = `lives` - 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": [ + "lives", + "departures" + ], + "type": "object" +} - removed
Output schema / $defs / ParticipantInfo / descriptionRemoved value: -"DDS participant discovered on the configured domain.\n\nCarries identity (`guid`, `name`, `vendor`) plus lifecycle fields\n(`first_seen_ns`, `last_seen_ns`, `status`, `seen_count`)." - changed
Output schema / $defs / ParticipantInfo / properties / announced_ns / descriptionPrevious value: -"Nanoseconds since epoch (DDS source timestamp of the participant's most recent announcement, taken from the announcing side's clock; the local clock when the announcer sent none). `None` when the backend does not expose it. Distinct from `first_seen_ns` / `last_seen_ns`, which are TopicForge's own local clock."New value: +"Source timestamp of the latest announcement, on the announcer's clock; `None` if unavailable." - removed
Output schema / $defs / ParticipantInfo / properties / announced_ns / titleRemoved value: -"Announced Ns" - removed
Output schema / $defs / ParticipantInfo / properties / domain_id / descriptionRemoved value: -"DDS domain id the participant is bound to." - removed
Output schema / $defs / ParticipantInfo / properties / domain_id / titleRemoved value: -"Domain Id" - changed
Output schema / $defs / ParticipantInfo / properties / first_seen_ns / descriptionPrevious value: -"Wall-clock timestamp (nanoseconds since epoch) of the **first** discovery sample TopicForge observed for this participant. `None` when the adapter does not track lifecycle."New value: +"Wall-clock ns of the first discovery sample seen; `None` without lifecycle tracking." - removed
Output schema / $defs / ParticipantInfo / properties / first_seen_ns / titleRemoved value: -"First Seen Ns" - removed
Output schema / $defs / ParticipantInfo / properties / guid / descriptionRemoved value: -"DDS Global Unique Identifier of the participant: hex string, stable across discovery events within a single deployment." - removed
Output schema / $defs / ParticipantInfo / properties / guid / titleRemoved value: -"Guid" - removed
Output schema / $defs / ParticipantInfo / properties / hostname / descriptionRemoved value: -"Hostname announced in discovery (Cyclone `__Hostname` property). `None` when the vendor does not announce it." - removed
Output schema / $defs / ParticipantInfo / properties / hostname / titleRemoved value: -"Hostname" - changed
Output schema / $defs / ParticipantInfo / properties / is_observer / descriptionPrevious value: -"True for TopicForge's own read-only observer participant, which joins the domain to watch it and appears in this list; false for every other participant."New value: +"True for TopicForge's own read-only observer participant." - removed
Output schema / $defs / ParticipantInfo / properties / is_observer / titleRemoved value: -"Is Observer" - changed
Output schema / $defs / ParticipantInfo / properties / last_seen_ns / descriptionPrevious value: -"Wall-clock timestamp (nanoseconds since epoch) of the **most recent** discovery sample observed. Used by `participant_events` to distinguish currently-active participants from those that left the bus. `None` when the adapter does not track lifecycle."New value: +"Wall-clock ns of the latest discovery sample; `None` without lifecycle tracking." - removed
Output schema / $defs / ParticipantInfo / properties / last_seen_ns / titleRemoved value: -"Last Seen Ns" - changed
Output schema / $defs / ParticipantInfo / properties / lost_ns / descriptionPrevious value: -"Nanoseconds since epoch of the moment the participant left, once `status` is `left`; `None` while it is active. How precise it is depends on `lost_time_source`. It is an upper bound of when the participant died: after a clean shutdown it is the exact leave time, after a crash it is when the lease expired, and the two cannot be told apart. The process died at or before `lost_ns`, and at most one lease earlier (10 s Cyclone default, 20 s Fast, 100 s RTI)."New value: +"Once `left`: ns upper bound of the death (exact after a clean shutdown; after a crash the lease expiry, up to one lease earlier: 10 s Cyclone, 20 s Fast, 100 s RTI). `None` while active." - removed
Output schema / $defs / ParticipantInfo / properties / lost_ns / titleRemoved value: -"Lost Ns" - changed
Output schema / $defs / ParticipantInfo / properties / lost_time_source / descriptionPrevious value: -"Where `lost_ns` comes from: `dds_source_timestamp` (timestamp carried by the discovery dispose) or `observed_local` (the moment TopicForge noticed, the weakest). `None` while active."New value: +"Origin of `lost_ns`: `dds_source_timestamp` or `observed_local` (weakest); `None` while active." - removed
Output schema / $defs / ParticipantInfo / properties / lost_time_source / titleRemoved value: -"Lost Time Source" - removed
Output schema / $defs / ParticipantInfo / properties / mode_effectiveRemoved value: -{ - "description": "Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`.", - "enum": [ - "mock", - "live" - ], - "title": "Mode Effective", - "type": "string" -} - changed
Output schema / $defs / ParticipantInfo / properties / name / descriptionPrevious value: -"Participant name the application announced through the standard DDS EntityName QoS (PID_ENTITY_NAME in RTPS discovery), for example `lidar_driver`. `None` when the application did not set one (Dust DDS cannot) or the backend does not expose it."New value: +"Announced EntityName (e.g. `lidar_driver`); `None` when the application set none (Dust DDS cannot)." - removed
Output schema / $defs / ParticipantInfo / properties / name / titleRemoved value: -"Name" - added
Output schema / $defs / ParticipantInfo / properties / node_namesAdded value: +{ + "description": "Fully qualified ROS 2 node names sharing this participant; empty when `node_names_source` is `none`.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / $defs / ParticipantInfo / properties / node_names_sourceAdded value: +{ + "default": "none", + "description": "`ros_discovery_info`, `ros2_cli` or `none` (no link made).", + "enum": [ + "ros_discovery_info", + "ros2_cli", + "none" + ], + "type": "string" +} - changed
Output schema / $defs / ParticipantInfo / properties / seen_count / descriptionPrevious value: -"Number of distinct discovery samples observed for this participant across all calls to `list_participants` during this server's lifetime. Starts at 1 and increments on each observation."New value: +"Discovery observations of this GUID in this server's lifetime. Not a restart count: a restart is a new GUID (see `restarts_by_name`)." - removed
Output schema / $defs / ParticipantInfo / properties / seen_count / titleRemoved value: -"Seen Count" - changed
Output schema / $defs / ParticipantInfo / properties / status / descriptionPrevious value: -"Lifecycle status. `active` means TopicForge has a recent discovery sample for this GUID and the bus has not signalled removal. `left` means the participant was observed earlier but has since disappeared (a Fast DDS `REMOVED` callback or a Cyclone polling delta). `unknown` when the adapter does not track lifecycle."New value: +"`active`, `left` (seen earlier, since gone) or `unknown` (no lifecycle tracking)." - removed
Output schema / $defs / ParticipantInfo / properties / status / titleRemoved value: -"Status" - changed
Output schema / $defs / ParticipantInfo / properties / vendor / descriptionPrevious value: -"DDS implementation that announced this participant, decoded from the OMG-RTPS `vendor_id` field on the discovery sample. `cyclone` (Eclipse Cyclone DDS), `fast` (eProsima Fast DDS), `rti` (RTI Connext), `rti_micro` (RTI Connext Micro), `opensplice` (ADLink OpenSplice), `opendds` (OCI OpenDDS), `coredx` (Twin Oaks CoreDX), `intercom` (Kongsberg InterCOM), `dust` (S2E Dust DDS). `mock` is reserved for synthetic fixtures. `unknown` means the vendor could not be determined: TopicForge reads it from the vendor prefix of the participant GUID, and some vendors (for example Dust DDS and RTI Connext) do not put their vendor id there, while the Cyclone Python binding does not expose the RTPS header vendor id. The participant is still reported; see `vendor_source`. Any conformant DDS-RTPS participant should appear through standard discovery; Cyclone DDS and Dust DDS have been observed on a live bus. See `docs/dds-interop-matrix.md`."New value: +"DDS implementation read from the GUID vendor prefix: cyclone, fast, rti, rti_micro, opensplice, opendds, coredx, intercom, dust, mock or `unknown` (Dust DDS and RTI Connext do not put theirs there); see `vendor_source`." - removed
Output schema / $defs / ParticipantInfo / properties / vendor / titleRemoved value: -"Vendor" - changed
Output schema / $defs / ParticipantInfo / properties / vendor_source / descriptionPrevious value: -"Where `vendor` came from: `guid_prefix` when it was read from the vendor prefix of the participant GUID, `none` when the vendor is unknown (or synthetic in mock mode)."New value: +"`guid_prefix` when `vendor` was read from the GUID, else `none`." - removed
Output schema / $defs / ParticipantInfo / properties / vendor_source / titleRemoved value: -"Vendor Source" - changed
Output schema / $defs / ParticipantInfo / requiredPrevious value: -[ - "guid", - "vendor", - "domain_id", - "mode_effective" -]New value: +[ + "guid", + "vendor", + "domain_id" +] - removed
Output schema / $defs / ParticipantInfo / titleRemoved value: -"ParticipantInfo" - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / descriptionAdded value: +"Envelope returned by `list_participants`." - added
Output schema / properties / domain_idAdded value: +{ + "description": "DDS domain observed (joined at startup).", + "maximum": 232, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / mode_effectiveAdded value: +{ + "description": "`live` or `mock` (fixtures).", + "enum": [ + "mock", + "live" + ], + "type": "string" +} - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - added
Output schema / properties / participantsAdded value: +{ + "items": { + "$ref": "#/$defs/ParticipantInfo" + }, + "type": "array" +} - added
Output schema / properties / restarts_by_nameAdded value: +{ + "additionalProperties": { + "$ref": "#/$defs/NameLifecycle" + }, + "description": "Names seen with several GUIDs or lost at least once (absent: ran once and stayed). A restart is a new GUID with the same name.", + "type": "object" +} - added
Output schema / properties / restarts_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Set when unnamed participants were left out of `restarts_by_name`." +} - removed
Output schema / properties / resultRemoved value: -{ - "items": { - "$ref": "#/$defs/ParticipantInfo" - }, - "title": "Result", - "type": "array" -} - added
Output schema / properties / returnedAdded value: +{ + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / totalAdded value: +{ + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / truncatedAdded value: +{ + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "participants", + "returned", + "total", + "truncated", + "domain_id", + "mode_effective" +] - changed
Output schema / titlePrevious value: -"list_participantsOutput"New value: +"ParticipantListing"
- Changed
list_topics15 fields changed- removed
Input schema / titleRemoved value: -"list_topicsArguments" - removed
Output schema / $defs / QosProfileRemoved value: -{ - "additionalProperties": false, - "description": "QoS profile of one DDS endpoint (reader or writer).\n\nCovers the policies behind most \"subscriber receives nothing\" cases.\nVendor-specific extensions are ignored.", - "properties": { - "data_representation": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Data representations the endpoint accepts or offers, from `XCDR1` and `XCDR2`. `None` when not announced.", - "title": "Data Representation" - }, - "deadline_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Deadline QoS in nanoseconds. `None` means no deadline (infinite, the DDS default). A reader deadline tighter (smaller) than a writer deadline is incompatible: the writer cannot guarantee the reader's promise.", - "title": "Deadline Ns" - }, - "destination_order": { - "anyOf": [ - { - "enum": [ - "BY_RECEPTION_TIMESTAMP", - "BY_SOURCE_TIMESTAMP" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DestinationOrder QoS. `None` when not announced.", - "title": "Destination Order" - }, - "durability": { - "description": "DDS Durability QoS. `VOLATILE` writers do not retain samples for late joiners; `TRANSIENT_LOCAL` writers do. A `TRANSIENT_LOCAL` reader cannot match a `VOLATILE` writer.", - "enum": [ - "VOLATILE", - "TRANSIENT_LOCAL", - "TRANSIENT", - "PERSISTENT" - ], - "title": "Durability", - "type": "string" - }, - "history": { - "anyOf": [ - { - "enum": [ - "KEEP_LAST", - "KEEP_ALL" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DDS History QoS. `KEEP_LAST` keeps a bounded ring buffer of size `history_depth`; `KEEP_ALL` keeps every sample (memory permitting). Mixed `KEEP_ALL` reader with `KEEP_LAST` writer is risky but not strictly incompatible. `None` when it is not known: DDS discovery does not carry History, so it is reported only for TopicForge's own endpoints and for Cyclone DDS peers that set something other than the default (see `history_note`).", - "title": "History" - }, - "history_depth": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Depth for `KEEP_LAST`. `None` for `KEEP_ALL` or when `history` is `None`.", - "title": "History Depth" - }, - "history_note": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Set when `history` was left out because discovery did not announce it.", - "title": "History Note" - }, - "latency_budget_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "LatencyBudget QoS in nanoseconds (a delivery-delay hint). `0` is the DDS default. `None` means infinite or not announced.", - "title": "Latency Budget Ns" - }, - "liveliness_kind": { - "anyOf": [ - { - "enum": [ - "AUTOMATIC", - "MANUAL_BY_PARTICIPANT", - "MANUAL_BY_TOPIC" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Liveliness QoS kind. `AUTOMATIC` is asserted by the middleware; the two `MANUAL_*` kinds need the application to write or assert liveliness, and the endpoint is declared not alive when it does not within `liveliness_lease_ns`. `None` when not announced.", - "title": "Liveliness Kind" - }, - "liveliness_lease_ns": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Liveliness lease duration in nanoseconds. `None` means infinite (the DDS default) or not announced.", - "title": "Liveliness Lease Ns" - }, - "ownership_kind": { - "anyOf": [ - { - "enum": [ - "SHARED", - "EXCLUSIVE" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Ownership QoS kind. With `EXCLUSIVE`, only the writer with the highest `ownership_strength` delivers samples to a reader. `None` when not announced.", - "title": "Ownership Kind" - }, - "ownership_strength": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Ownership strength of a writer (`EXCLUSIVE` ownership). `None` when not announced.", - "title": "Ownership Strength" - }, - "partitions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Partition names the endpoint's Publisher or Subscriber belongs to. No Partition policy is the default partition, reported as `[\"\"]` (never an empty list). Endpoints only match when their partitions intersect. `None` only when the backend cannot read partitions at all (unknown, not the default).", - "title": "Partitions" - }, - "reliability": { - "description": "DDS Reliability QoS. `RELIABLE` retries lost samples; `BEST_EFFORT` does not. A `RELIABLE` reader cannot match a `BEST_EFFORT` writer.", - "enum": [ - "RELIABLE", - "BEST_EFFORT" - ], - "title": "Reliability", - "type": "string" - }, - "unknown_policies": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Policies the endpoint announced but whose value could not be read (for example `Deadline`). Their duration fields are `None` here, which does NOT mean infinite for these: the scan does not compare them.", - "title": "Unknown Policies" - } - }, - "required": [ - "reliability", - "durability" - ], - "title": "QosProfile", - "type": "object" -} - removed
Output schema / $defs / TopicInfoRemoved value: -{ - "additionalProperties": false, - "description": "Description of a single ROS2 topic.\n\nCarries optional DDS-side enrichment fields when the active middleware\nbackend can resolve them (CycloneDDS / Fast DDS). The ROS2 CLI adapter and\nthe mock ROS2 path leave them `None`.", - "properties": { - "message_type": { - "description": "ROS2 message type, e.g. `geometry_msgs/msg/Twist`.", - "title": "Message Type", - "type": "string" - }, - "mode_effective": { - "description": "Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`.", - "enum": [ - "mock", - "live" - ], - "title": "Mode Effective", - "type": "string" - }, - "name": { - "description": "Fully qualified topic name, e.g. `/cmd_vel`.", - "title": "Name", - "type": "string" - }, - "publisher_count": { - "description": "Publishers known to the graph.", - "minimum": 0, - "title": "Publisher Count", - "type": "integer" - }, - "qos_durability": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Durability announced by the topic's publishers: `volatile`, `transient_local` (late subscribers receive the last samples; typical of latched topics such as `/tf_static`), or `mixed` when publishers disagree. `null` when unknown, with the same rules as `qos_reliability`.", - "title": "Qos Durability" - }, - "qos_profile": { - "anyOf": [ - { - "$ref": "#/$defs/QosProfile" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Effective DDS QoS profile for this topic when resolvable. `None` from the ROS2 CLI adapter or when the DDS module is inactive. The DDS module populates this on a best-effort basis (picks one representative endpoint if reader/writer QoS differ)." - }, - "qos_reliability": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Reliability announced by the topic's publishers: `reliable`, `best_effort`, or `mixed` when publishers disagree. `null` when unknown: the topic has no publisher, or the value was not read (`list_topics` does not read QoS; `get_topic_info` does).", - "title": "Qos Reliability" - }, - "reader_count": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DDS reader-endpoint count when the active backend can resolve endpoint-level info (Cyclone / Fast DDS). `None` from the ROS2 CLI adapter or when the DDS module is inactive.", - "title": "Reader Count" - }, - "subscriber_count": { - "description": "Subscribers known to the graph.", - "minimum": 0, - "title": "Subscriber Count", - "type": "integer" - }, - "writer_count": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "DDS writer-endpoint count when the active backend can resolve endpoint-level info. `None` from the ROS2 CLI adapter or when the DDS module is inactive.", - "title": "Writer Count" - } - }, - "required": [ - "name", - "message_type", - "publisher_count", - "subscriber_count", - "mode_effective" - ], - "title": "TopicInfo", - "type": "object" -} - added
Output schema / $defs / TopicListItemAdded value: +{ + "additionalProperties": false, + "properties": { + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "publisher_count": { + "minimum": 0, + "type": "integer" + }, + "subscriber_count": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "name", + "message_type", + "publisher_count", + "subscriber_count" + ], + "type": "object" +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / descriptionAdded value: +"Envelope returned by `list_topics`." - added
Output schema / properties / mode_effectiveAdded value: +{ + "description": "`live` or `mock` (fixtures).", + "enum": [ + "mock", + "live" + ], + "type": "string" +} - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - removed
Output schema / properties / resultRemoved value: -{ - "items": { - "$ref": "#/$defs/TopicInfo" - }, - "title": "Result", - "type": "array" -} - added
Output schema / properties / returnedAdded value: +{ + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / topicsAdded value: +{ + "items": { + "$ref": "#/$defs/TopicListItem" + }, + "type": "array" +} - added
Output schema / properties / totalAdded value: +{ + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / truncatedAdded value: +{ + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "topics", + "returned", + "total", + "truncated", + "mode_effective" +] - changed
Output schema / titlePrevious value: -"list_topicsOutput"New value: +"TopicListing"
- Changed
participant_events43 fields changed- changed
Input schema / properties / domain_id / descriptionPrevious value: -"Accepted for compatibility (0..232). TopicForge observes the domain it joined at startup (TOPICFORGE_DDS_DOMAIN_ID); this argument does not switch domains, and the response `domain_id` says which one was observed."New value: +"Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value." - removed
Input schema / properties / domain_id / titleRemoved value: -"Domain Id" - added
Input schema / properties / lookback_sAdded value: +{ + "default": 300, + "description": "Window in seconds, 1..86400, default 300. A larger window may hit the 200-event cap.", + "maximum": 86400, + "minimum": 1, + "type": "integer" +} - removed
Input schema / properties / lookback_secondsRemoved value: -{ - "default": 300, - "description": "Window (in seconds) over which to return events. Defaults to 300 (5 minutes). Range: 1..86400 (1 second to 24 hours). Larger windows may hit the 200-event cap: narrow the window or filter on `domain_id` when that happens.", - "maximum": 86400, - "minimum": 1, - "title": "Lookback Seconds", - "type": "integer" -} - removed
Input schema / titleRemoved value: -"participant_eventsArguments" - added
Output schema / $defs / NameLifecycleAdded value: +{ + "additionalProperties": false, + "properties": { + "departures": { + "description": "Times a participant with this name was lost.", + "minimum": 0, + "type": "integer" + }, + "lives": { + "description": "Distinct GUIDs that carried this name; restarts = `lives` - 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": [ + "lives", + "departures" + ], + "type": "object" +} - removed
Output schema / $defs / ParticipantEvent / descriptionRemoved value: -"A point-in-time lifecycle event (discovered or lost) for a DDS participant.\n\n`ParticipantInfo` is the current state; this is one event in its history.\nReturned by `participant_events`." - added
Output schema / $defs / ParticipantEvent / properties / age_sAdded value: +{ + "anyOf": [ + { + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Seconds between `timestamp_ns` and this response; `None` in mock mode." +} - removed
Output schema / $defs / ParticipantEvent / properties / domain_id / descriptionRemoved value: -"DDS domain id the event occurred on." - removed
Output schema / $defs / ParticipantEvent / properties / domain_id / titleRemoved value: -"Domain Id" - changed
Output schema / $defs / ParticipantEvent / properties / event_type / descriptionPrevious value: -"`discovered` when the participant first joined the bus (or rejoined after leaving). `lost` when TopicForge observed a `REMOVED` callback (Fast DDS) or a polling delta where the GUID no longer appears in the DCPSParticipant builtin reader snapshot (Cyclone). The transition is reported once per state change."New value: +"`discovered` (joined or rejoined) or `lost` (removed, or gone from discovery); reported once per change." - removed
Output schema / $defs / ParticipantEvent / properties / event_type / titleRemoved value: -"Event Type" - removed
Output schema / $defs / ParticipantEvent / properties / guid / descriptionRemoved value: -"DDS Global Unique Identifier of the participant involved." - removed
Output schema / $defs / ParticipantEvent / properties / guid / titleRemoved value: -"Guid" - removed
Output schema / $defs / ParticipantEvent / properties / hostname / descriptionRemoved value: -"Hostname announced by the participant when known, else `None`." - removed
Output schema / $defs / ParticipantEvent / properties / hostname / titleRemoved value: -"Hostname" - removed
Output schema / $defs / ParticipantEvent / properties / mode_effectiveRemoved value: -{ - "description": "Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`.", - "enum": [ - "mock", - "live" - ], - "title": "Mode Effective", - "type": "string" -} - removed
Output schema / $defs / ParticipantEvent / properties / name / descriptionRemoved value: -"Participant name (EntityName QoS) when known, else `None`." - removed
Output schema / $defs / ParticipantEvent / properties / name / titleRemoved value: -"Name" - changed
Output schema / $defs / ParticipantEvent / properties / observed_ns / descriptionPrevious value: -"Local wall-clock time (ns since epoch) at which TopicForge noticed the event. Always at or after `timestamp_ns` when the latter is DDS-derived. `None` when not tracked."New value: +"Local ns when TopicForge noticed the event, never before a DDS-derived `timestamp_ns`; `None` when untracked." - removed
Output schema / $defs / ParticipantEvent / properties / observed_ns / titleRemoved value: -"Observed Ns" - changed
Output schema / $defs / ParticipantEvent / properties / time_source / descriptionPrevious value: -"Where `timestamp_ns` comes from. `dds_source_timestamp`: the DDS timestamp of the discovery announcement or dispose. For a `lost` event it is an upper bound: exact after a clean shutdown, the lease expiry after a crash (the process died up to one lease earlier: 10 s Cyclone default, 20 s Fast, 100 s RTI), and the two cannot be told apart. `observed_local`: the moment TopicForge noticed (weakest)."New value: +"`dds_source_timestamp` (DDS timestamp of the announcement or dispose; for `lost` an upper bound of the death) or `observed_local` (when TopicForge noticed, weakest)." - removed
Output schema / $defs / ParticipantEvent / properties / time_source / titleRemoved value: -"Time Source" - changed
Output schema / $defs / ParticipantEvent / properties / timestamp_ns / descriptionPrevious value: -"Wall-clock timestamp (nanoseconds since epoch) when TopicForge captured the event. For Fast DDS this is when the listener callback ran; for Cyclone this is when the polling delta was computed; for mock fixtures this is a deterministic anchor."New value: +"Wall-clock ns of the event; its origin is `time_source`." - removed
Output schema / $defs / ParticipantEvent / properties / timestamp_ns / titleRemoved value: -"Timestamp Ns" - removed
Output schema / $defs / ParticipantEvent / properties / vendor / descriptionRemoved value: -"DDS implementation tag for the participant involved, decoded the same way as `ParticipantInfo.vendor`." - removed
Output schema / $defs / ParticipantEvent / properties / vendor / titleRemoved value: -"Vendor" - changed
Output schema / $defs / ParticipantEvent / requiredPrevious value: -[ - "guid", - "event_type", - "vendor", - "timestamp_ns", - "domain_id", - "mode_effective" -]New value: +[ + "guid", + "event_type", + "vendor", + "timestamp_ns", + "domain_id" +] - removed
Output schema / $defs / ParticipantEvent / titleRemoved value: -"ParticipantEvent" - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / descriptionAdded value: +"Envelope returned by `participant_events`." - added
Output schema / properties / domain_idAdded value: +{ + "description": "DDS domain observed (joined at startup).", + "maximum": 232, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / eventsAdded value: +{ + "items": { + "$ref": "#/$defs/ParticipantEvent" + }, + "type": "array" +} - added
Output schema / properties / mode_effectiveAdded value: +{ + "description": "`live` or `mock` (fixtures).", + "enum": [ + "mock", + "live" + ], + "type": "string" +} - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - added
Output schema / properties / restarts_by_nameAdded value: +{ + "additionalProperties": { + "$ref": "#/$defs/NameLifecycle" + }, + "description": "Names seen with several GUIDs or lost at least once (absent: ran once and stayed). A restart is a new GUID with the same name. Window only.", + "type": "object" +} - added
Output schema / properties / restarts_noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Set when unnamed participants were left out of `restarts_by_name`." +} - removed
Output schema / properties / resultRemoved value: -{ - "items": { - "$ref": "#/$defs/ParticipantEvent" - }, - "title": "Result", - "type": "array" -} - added
Output schema / properties / returnedAdded value: +{ + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / totalAdded value: +{ + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / truncatedAdded value: +{ + "description": "True when cut to the 200-event cap.", + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "events", + "returned", + "total", + "truncated", + "domain_id", + "mode_effective" +] - changed
Output schema / titlePrevious value: -"participant_eventsOutput"New value: +"ParticipantEventListing"
- Changed
peek_bag_samples51 fields changed- changed
Input schema / properties / count / descriptionPrevious value: -"Maximum number of recent messages to return. Defaults to 5; silently clamped to 50 (the hard cap that keeps tool output bounded; read it from `health_check.max_sample_count`). Negative values raise an error. The returned `SampleResult.count` reflects the actual number of samples produced: it can be lower than the request (empty topic, timeout, mock fixture shorter than requested)."New value: +"Messages to return, default 5, at most 50 (`health_check.max_sample_count`): more is capped to 50, negative raises an error. The result's `count` may be lower." - removed
Input schema / properties / count / titleRemoved value: -"Count" - changed
Input schema / properties / path / descriptionPrevious value: -"Path to a bag: a file ending in `.mcap` or `.db3`, or a `rosbag2_*` directory. `peek_bag_samples` also reads ROS 1 `.bag` files; `analyze_bag` does not (`ros2 bag info` cannot open them). Leading/trailing whitespace is stripped. Null bytes and otherwise malformed filesystem paths are rejected. Existence and bag format are validated by the live adapter (mock mode accepts any well-formed path)."New value: +"Bag path: a `.mcap` or `.db3` file or a `rosbag2_*` directory (`peek_bag_samples` also reads ROS 1 `.bag`; `analyze_bag` does not). Existence and format are checked live; mock accepts any well-formed path." - removed
Input schema / properties / path / titleRemoved value: -"Path" - changed
Input schema / properties / topic / descriptionPrevious value: -"Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and underscores; everything else (whitespace, quotes, shell metacharacters, `//`, trailing `/`) is rejected before reaching the `ros2` CLI."New value: +"Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - removed
Input schema / titleRemoved value: -"peek_bag_samplesArguments" - added
Output schema / $defs / ImageSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "data_length": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixel buffer size in bytes (see `data_length_basis`)." + }, + "data_length_basis": { + "anyOf": [ + { + "enum": [ + "payload", + "step_x_height" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)." + }, + "encoding": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Pixel encoding, e.g. `rgb8`." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the camera." + }, + "height": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixels." + }, + "is_bigendian": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Big-endian pixels." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when partial; else `null`." + }, + "step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Row length, bytes." + }, + "summary_type": { + "const": "image", + "type": "string" + }, + "width": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixels." + } + }, + "required": [ + "summary_type", + "frame_id", + "width", + "height", + "encoding", + "step", + "is_bigendian", + "data_length", + "data_length_basis" + ], + "type": "object" +} - added
Output schema / $defs / ImuSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angular_velocity_norm": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `angular_velocity`, rad/s." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the IMU." + }, + "linear_acceleration_norm": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `linear_acceleration`, m/s^2." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when a value is unusable; else `null`." + }, + "orientation_valid": { + "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.", + "type": "boolean" + }, + "pitch": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about y, radians." + }, + "roll": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about x, radians." + }, + "summary_type": { + "const": "imu", + "type": "string" + }, + "yaw": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about z, radians." + } + }, + "required": [ + "summary_type", + "frame_id", + "orientation_valid", + "roll", + "pitch", + "yaw", + "angular_velocity_norm", + "linear_acceleration_norm" + ], + "type": "object" +} - added
Output schema / $defs / LaserScanSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angle_increment": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Angle between beams, rad." + }, + "angle_max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Bearing of the last beam, rad." + }, + "angle_min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Bearing of beam 0, rad." + }, + "beam_count": { + "description": "Beams in `ranges`.", + "minimum": 0, + "type": "integer" + }, + "closest_obstacle": { + "anyOf": [ + { + "$ref": "#/$defs/ScanReturn" + }, + { + "type": "null" + } + ], + "description": "Closest valid return of the scan; `null` with `note`." + }, + "finite_count": { + "description": "Beams with a finite range.", + "minimum": 0, + "type": "integer" + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame." + }, + "inf_count": { + "description": "Beams at `+inf`.", + "minimum": 0, + "type": "integer" + }, + "nan_count": { + "description": "Beams at `nan`.", + "minimum": 0, + "type": "integer" + }, + "neg_inf_count": { + "description": "Beams at `-inf`.", + "minimum": 0, + "type": "integer" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when the summary is partial; else `null`." + }, + "out_of_range_count": { + "description": "Finite ranges outside `range_min`..`range_max` (not valid).", + "minimum": 0, + "type": "integer" + }, + "range_max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Largest valid range, m." + }, + "range_min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Smallest valid range, m." + }, + "sectors": { + "$ref": "#/$defs/ScanSectors", + "description": "Closest valid return per sector." + }, + "summary_type": { + "const": "laser_scan", + "type": "string" + } + }, + "required": [ + "summary_type", + "frame_id", + "beam_count", + "angle_min", + "angle_max", + "angle_increment", + "range_min", + "range_max", + "finite_count", + "inf_count", + "neg_inf_count", + "nan_count", + "out_of_range_count", + "closest_obstacle", + "sectors" + ], + "type": "object" +} - removed
Output schema / $defs / MessageSample / descriptionRemoved value: -"A single sampled message on a topic." - removed
Output schema / $defs / MessageSample / properties / message_type / descriptionRemoved value: -"ROS2 message type of this sample, e.g. `geometry_msgs/msg/Twist`." - removed
Output schema / $defs / MessageSample / properties / message_type / titleRemoved value: -"Message Type" - changed
Output schema / $defs / MessageSample / properties / payload / descriptionPrevious value: -"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded."New value: +"Message fields as nested values; `nan`/`inf` are strings. Live `sample_messages` adds `_truncated_fields` (cut paths) and `_raw_text` (unparsable output)." - removed
Output schema / $defs / MessageSample / properties / payload / titleRemoved value: -"Payload" - changed
Output schema / $defs / MessageSample / properties / received_ns / descriptionPrevious value: -"Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise."New value: +"Wall-clock ns when the `ros2` CLI printed the message (live `sample_messages` only)." - removed
Output schema / $defs / MessageSample / properties / received_ns / titleRemoved value: -"Received Ns" - changed
Output schema / $defs / MessageSample / properties / recorded_ns / descriptionPrevious value: -"Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role."New value: +"Bag record time in ns (`peek_bag_samples` only)." - removed
Output schema / $defs / MessageSample / properties / recorded_ns / titleRemoved value: -"Recorded Ns" - removed
Output schema / $defs / MessageSample / properties / stamp_source / anyOfRemoved value: -[ - { - "enum": [ - "header", - "none", - "recorded" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / stamp_source / defaultRemoved value: -null - changed
Output schema / $defs / MessageSample / properties / stamp_source / descriptionPrevious value: -"Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say."New value: +"Origin of `timestamp_ns`: `header`, `payload`, `recorded` (bag record time), `dds_source` or `none` (0)." - added
Output schema / $defs / MessageSample / properties / stamp_source / enumAdded value: +[ + "header", + "payload", + "recorded", + "dds_source", + "none" +] - removed
Output schema / $defs / MessageSample / properties / stamp_source / titleRemoved value: -"Stamp Source" - added
Output schema / $defs / MessageSample / properties / stamp_source / typeAdded value: +"string" - added
Output schema / $defs / MessageSample / properties / summaryAdded value: +{ + "anyOf": [ + { + "discriminator": { + "mapping": { + "image": "#/$defs/ImageSummary", + "imu": "#/$defs/ImuSummary", + "laser_scan": "#/$defs/LaserScanSummary", + "odometry": "#/$defs/OdometrySummary", + "point_cloud2": "#/$defs/PointCloud2Summary" + }, + "propertyName": "summary_type" + }, + "oneOf": [ + { + "$ref": "#/$defs/LaserScanSummary" + }, + { + "$ref": "#/$defs/OdometrySummary" + }, + { + "$ref": "#/$defs/ImuSummary" + }, + { + "$ref": "#/$defs/ImageSummary" + }, + { + "$ref": "#/$defs/PointCloud2Summary" + } + ] + }, + { + "type": "null" + } + ], + "default": null, + "description": "Condensed reading of the whole message (see `summary_type`); `null` for other types and DDS peeks." +} - changed
Output schema / $defs / MessageSample / properties / timestamp_ns / descriptionPrevious value: -"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."New value: +"Publisher-clock ns: `header.stamp`, else the time in the body of `Clock`, `TFMessage` or `Log`, else 0 (see `stamp_source`); not the arrival time." - removed
Output schema / $defs / MessageSample / properties / timestamp_ns / titleRemoved value: -"Timestamp Ns" - removed
Output schema / $defs / MessageSample / properties / topic / descriptionRemoved value: -"Fully qualified topic name the sample was taken from, e.g. `/cmd_vel`." - removed
Output schema / $defs / MessageSample / properties / topic / titleRemoved value: -"Topic" - changed
Output schema / $defs / MessageSample / requiredPrevious value: -[ - "topic", - "message_type", - "timestamp_ns" -]New value: +[ + "topic", + "message_type", + "timestamp_ns", + "stamp_source" +] - removed
Output schema / $defs / MessageSample / titleRemoved value: -"MessageSample" - added
Output schema / $defs / OdometrySummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angular_z": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`twist.twist.angular.z`, rad/s." + }, + "child_frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`child_frame_id` (frame of the twist)." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` (frame of the pose)." + }, + "linear_speed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `twist.twist.linear`, m/s." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when a value is missing; else `null`." + }, + "position_x": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.x`, m." + }, + "position_y": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.y`, m." + }, + "position_z": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.z`, m." + }, + "summary_type": { + "const": "odometry", + "type": "string" + }, + "yaw": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Heading from the pose quaternion, radians." + } + }, + "required": [ + "summary_type", + "frame_id", + "child_frame_id", + "linear_speed", + "angular_z", + "position_x", + "position_y", + "position_z", + "yaw" + ], + "type": "object" +} - added
Output schema / $defs / PointCloud2SummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "fields": { + "description": "Point layout.", + "items": { + "$ref": "#/$defs/PointFieldSummary" + }, + "type": "array" + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the cloud." + }, + "height": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows (1: unorganized)." + }, + "is_dense": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "True when no point is invalid." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when partial; else `null`." + }, + "point_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "`width` times `height`." + }, + "point_step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Bytes per point." + }, + "row_step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Bytes per row." + }, + "summary_type": { + "const": "point_cloud2", + "type": "string" + }, + "width": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Points per row." + } + }, + "required": [ + "summary_type", + "frame_id", + "width", + "height", + "point_count", + "point_step", + "row_step", + "is_dense", + "fields" + ], + "type": "object" +} - added
Output schema / $defs / PointFieldSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Elements per point." + }, + "datatype": { + "description": "Element type, e.g. `FLOAT32`.", + "type": "string" + }, + "name": { + "description": "Field name, e.g. `x`.", + "type": "string" + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Byte offset in a point." + } + }, + "required": [ + "name", + "datatype", + "offset", + "count" + ], + "type": "object" +} - added
Output schema / $defs / ScanReturnAdded value: +{ + "additionalProperties": false, + "properties": { + "beam_index": { + "description": "Index in `ranges`.", + "minimum": 0, + "type": "integer" + }, + "bearing": { + "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.", + "type": "number" + }, + "range": { + "description": "Distance, meters.", + "type": "number" + } + }, + "required": [ + "range", + "bearing", + "beam_index" + ], + "type": "object" +} - added
Output schema / $defs / ScanSectorAdded value: +{ + "additionalProperties": false, + "properties": { + "beam_count": { + "description": "Beams inside the sector (0: not covered).", + "minimum": 0, + "type": "integer" + }, + "closest": { + "anyOf": [ + { + "$ref": "#/$defs/ScanReturn" + }, + { + "type": "null" + } + ], + "description": "Closest valid return in the sector; `null` with `note`." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `closest` is `null`; `null` when it is set." + } + }, + "required": [ + "beam_count", + "closest" + ], + "type": "object" +} - added
Output schema / $defs / ScanSectorsAdded value: +{ + "additionalProperties": false, + "properties": { + "front": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings within +-45 degrees of +x." + }, + "left": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings from 45 to 135 degrees." + }, + "rear": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings beyond +-135 degrees." + }, + "right": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings from -135 to -45 degrees." + } + }, + "required": [ + "front", + "left", + "right", + "rear" + ], + "type": "object" +} - added
Output schema / $defs / TopicRateAdded value: +{ + "additionalProperties": false, + "properties": { + "basis": { + "description": "Clock the rate is measured on: `received_ns` (live) or `recorded_ns` (bag).", + "enum": [ + "received_ns", + "recorded_ns" + ], + "type": "string" + }, + "interval_cv": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Interval standard deviation over mean; `null` under 2 messages." + }, + "interval_median_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Median interval; `null` under 2 messages." + }, + "max_gap_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Longest interval; `null` under 2 messages." + }, + "mean_interval_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Mean interval; `null` under 2 messages." + }, + "message_count": { + "description": "Messages the rate is computed from.", + "minimum": 0, + "type": "integer" + }, + "observed_frequency_hz": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Messages per second on `basis`, drain excluded; the rate the caller received. `null` under 2 messages." + }, + "sim_frequency_hz": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Same rate on the messages' own stamps (publisher clock); `null` without advancing stamps." + }, + "startup_burst_count": { + "description": "Leading messages set aside as a start-up drain (live only); still in `message_count`.", + "minimum": 0, + "type": "integer" + }, + "trailing_gap_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Time from the last message to the end of a live observation; else `null`." + }, + "verdict": { + "description": "First match: `silent` (none), `insufficient` (under 5), `intermittent` (an interval over 3x the median), `stable` (cv < 0.2), `jittery` (< 0.5), `erratic`. Describes this window, not a diagnosis.", + "enum": [ + "silent", + "insufficient", + "intermittent", + "stable", + "jittery", + "erratic" + ], + "type": "string" + }, + "verdict_note": { + "description": "The verdict in plain words.", + "type": "string" + }, + "window_s": { + "description": "Observation length, seconds.", + "minimum": 0, + "type": "number" + } + }, + "required": [ + "basis", + "message_count", + "startup_burst_count", + "window_s", + "mean_interval_s", + "interval_median_s", + "max_gap_s", + "interval_cv", + "observed_frequency_hz", + "sim_frequency_hz", + "trailing_gap_s", + "verdict", + "verdict_note" + ], + "type": "object" +} - changed
Output schema / descriptionPrevious value: -"Envelope returned by the `sample_messages` tool."New value: +"Samples returned by `sample_messages`, `peek_dds_samples` and `peek_bag_samples`." - changed
Output schema / properties / count / descriptionPrevious value: -"Number of samples actually returned. May be 0 (no publisher active in live mode, or empty mock fixture), less than the requested count (topic yielded fewer messages within the timeout), or capped by the the silent maximum of 50: request `count > 50` and you will receive at most 50 without warning."New value: +"Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50)." - removed
Output schema / properties / count / titleRemoved value: -"Count" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - changed
Output schema / properties / note / descriptionPrevious value: -"Why `samples` is empty or limited, when the cause is not obvious (for example payload decoding is disabled for DDS user topics). `None` when there is nothing to add."New value: +"Why `samples` is empty or limited, when not obvious; else `None`." - removed
Output schema / properties / note / titleRemoved value: -"Note" - added
Output schema / properties / rateAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/TopicRate" + }, + { + "type": "null" + } + ], + "default": null, + "description": "How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10." +} - changed
Output schema / properties / samples / descriptionPrevious value: -"The sampled messages, ordered as received from the backend."New value: +"The samples, in backend order." - removed
Output schema / properties / samples / titleRemoved value: -"Samples" - removed
Output schema / properties / topic / descriptionRemoved value: -"Topic the samples were taken from, echoed from the request." - removed
Output schema / properties / topic / titleRemoved value: -"Topic"
- Changed
peek_dds_samples49 fields changed- changed
Input schema / properties / count / descriptionPrevious value: -"Maximum number of recent messages to return. Defaults to 5; silently clamped to 50 (the hard cap that keeps tool output bounded; read it from `health_check.max_sample_count`). Negative values raise an error. The returned `SampleResult.count` reflects the actual number of samples produced: it can be lower than the request (empty topic, timeout, mock fixture shorter than requested)."New value: +"Messages to return, default 5, at most 50 (`health_check.max_sample_count`): more is capped to 50, negative raises an error. The result's `count` may be lower." - removed
Input schema / properties / count / titleRemoved value: -"Count" - changed
Input schema / properties / topic / descriptionPrevious value: -"DDS topic name. Bare DDS names such as `scan` are valid, as are ROS 2 mangled names such as `rt/scan` and the builtin discovery topics `DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`. Letters, digits, `_`, `/` and `::` are allowed; anything else is rejected."New value: +"DDS topic name: bare (`scan`), ROS 2 mangled (`rt/scan`) or a builtin discovery topic (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Letters, digits, `_`, `/` and `::` only." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - removed
Input schema / titleRemoved value: -"peek_dds_samplesArguments" - added
Output schema / $defs / ImageSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "data_length": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixel buffer size in bytes (see `data_length_basis`)." + }, + "data_length_basis": { + "anyOf": [ + { + "enum": [ + "payload", + "step_x_height" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)." + }, + "encoding": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Pixel encoding, e.g. `rgb8`." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the camera." + }, + "height": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixels." + }, + "is_bigendian": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Big-endian pixels." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when partial; else `null`." + }, + "step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Row length, bytes." + }, + "summary_type": { + "const": "image", + "type": "string" + }, + "width": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixels." + } + }, + "required": [ + "summary_type", + "frame_id", + "width", + "height", + "encoding", + "step", + "is_bigendian", + "data_length", + "data_length_basis" + ], + "type": "object" +} - added
Output schema / $defs / ImuSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angular_velocity_norm": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `angular_velocity`, rad/s." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the IMU." + }, + "linear_acceleration_norm": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `linear_acceleration`, m/s^2." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when a value is unusable; else `null`." + }, + "orientation_valid": { + "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.", + "type": "boolean" + }, + "pitch": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about y, radians." + }, + "roll": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about x, radians." + }, + "summary_type": { + "const": "imu", + "type": "string" + }, + "yaw": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about z, radians." + } + }, + "required": [ + "summary_type", + "frame_id", + "orientation_valid", + "roll", + "pitch", + "yaw", + "angular_velocity_norm", + "linear_acceleration_norm" + ], + "type": "object" +} - added
Output schema / $defs / LaserScanSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angle_increment": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Angle between beams, rad." + }, + "angle_max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Bearing of the last beam, rad." + }, + "angle_min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Bearing of beam 0, rad." + }, + "beam_count": { + "description": "Beams in `ranges`.", + "minimum": 0, + "type": "integer" + }, + "closest_obstacle": { + "anyOf": [ + { + "$ref": "#/$defs/ScanReturn" + }, + { + "type": "null" + } + ], + "description": "Closest valid return of the scan; `null` with `note`." + }, + "finite_count": { + "description": "Beams with a finite range.", + "minimum": 0, + "type": "integer" + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame." + }, + "inf_count": { + "description": "Beams at `+inf`.", + "minimum": 0, + "type": "integer" + }, + "nan_count": { + "description": "Beams at `nan`.", + "minimum": 0, + "type": "integer" + }, + "neg_inf_count": { + "description": "Beams at `-inf`.", + "minimum": 0, + "type": "integer" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when the summary is partial; else `null`." + }, + "out_of_range_count": { + "description": "Finite ranges outside `range_min`..`range_max` (not valid).", + "minimum": 0, + "type": "integer" + }, + "range_max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Largest valid range, m." + }, + "range_min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Smallest valid range, m." + }, + "sectors": { + "$ref": "#/$defs/ScanSectors", + "description": "Closest valid return per sector." + }, + "summary_type": { + "const": "laser_scan", + "type": "string" + } + }, + "required": [ + "summary_type", + "frame_id", + "beam_count", + "angle_min", + "angle_max", + "angle_increment", + "range_min", + "range_max", + "finite_count", + "inf_count", + "neg_inf_count", + "nan_count", + "out_of_range_count", + "closest_obstacle", + "sectors" + ], + "type": "object" +} - removed
Output schema / $defs / MessageSample / descriptionRemoved value: -"A single sampled message on a topic." - removed
Output schema / $defs / MessageSample / properties / message_type / descriptionRemoved value: -"ROS2 message type of this sample, e.g. `geometry_msgs/msg/Twist`." - removed
Output schema / $defs / MessageSample / properties / message_type / titleRemoved value: -"Message Type" - changed
Output schema / $defs / MessageSample / properties / payload / descriptionPrevious value: -"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded."New value: +"Message fields as nested values; `nan`/`inf` are strings. Live `sample_messages` adds `_truncated_fields` (cut paths) and `_raw_text` (unparsable output)." - removed
Output schema / $defs / MessageSample / properties / payload / titleRemoved value: -"Payload" - changed
Output schema / $defs / MessageSample / properties / received_ns / descriptionPrevious value: -"Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise."New value: +"Wall-clock ns when the `ros2` CLI printed the message (live `sample_messages` only)." - removed
Output schema / $defs / MessageSample / properties / received_ns / titleRemoved value: -"Received Ns" - changed
Output schema / $defs / MessageSample / properties / recorded_ns / descriptionPrevious value: -"Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role."New value: +"Bag record time in ns (`peek_bag_samples` only)." - removed
Output schema / $defs / MessageSample / properties / recorded_ns / titleRemoved value: -"Recorded Ns" - removed
Output schema / $defs / MessageSample / properties / stamp_source / anyOfRemoved value: -[ - { - "enum": [ - "header", - "none", - "recorded" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / stamp_source / defaultRemoved value: -null - changed
Output schema / $defs / MessageSample / properties / stamp_source / descriptionPrevious value: -"Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say."New value: +"Origin of `timestamp_ns`: `header`, `payload`, `recorded` (bag record time), `dds_source` or `none` (0)." - added
Output schema / $defs / MessageSample / properties / stamp_source / enumAdded value: +[ + "header", + "payload", + "recorded", + "dds_source", + "none" +] - removed
Output schema / $defs / MessageSample / properties / stamp_source / titleRemoved value: -"Stamp Source" - added
Output schema / $defs / MessageSample / properties / stamp_source / typeAdded value: +"string" - added
Output schema / $defs / MessageSample / properties / summaryAdded value: +{ + "anyOf": [ + { + "discriminator": { + "mapping": { + "image": "#/$defs/ImageSummary", + "imu": "#/$defs/ImuSummary", + "laser_scan": "#/$defs/LaserScanSummary", + "odometry": "#/$defs/OdometrySummary", + "point_cloud2": "#/$defs/PointCloud2Summary" + }, + "propertyName": "summary_type" + }, + "oneOf": [ + { + "$ref": "#/$defs/LaserScanSummary" + }, + { + "$ref": "#/$defs/OdometrySummary" + }, + { + "$ref": "#/$defs/ImuSummary" + }, + { + "$ref": "#/$defs/ImageSummary" + }, + { + "$ref": "#/$defs/PointCloud2Summary" + } + ] + }, + { + "type": "null" + } + ], + "default": null, + "description": "Condensed reading of the whole message (see `summary_type`); `null` for other types and DDS peeks." +} - changed
Output schema / $defs / MessageSample / properties / timestamp_ns / descriptionPrevious value: -"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."New value: +"Publisher-clock ns: `header.stamp`, else the time in the body of `Clock`, `TFMessage` or `Log`, else 0 (see `stamp_source`); not the arrival time." - removed
Output schema / $defs / MessageSample / properties / timestamp_ns / titleRemoved value: -"Timestamp Ns" - removed
Output schema / $defs / MessageSample / properties / topic / descriptionRemoved value: -"Fully qualified topic name the sample was taken from, e.g. `/cmd_vel`." - removed
Output schema / $defs / MessageSample / properties / topic / titleRemoved value: -"Topic" - changed
Output schema / $defs / MessageSample / requiredPrevious value: -[ - "topic", - "message_type", - "timestamp_ns" -]New value: +[ + "topic", + "message_type", + "timestamp_ns", + "stamp_source" +] - removed
Output schema / $defs / MessageSample / titleRemoved value: -"MessageSample" - added
Output schema / $defs / OdometrySummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angular_z": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`twist.twist.angular.z`, rad/s." + }, + "child_frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`child_frame_id` (frame of the twist)." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` (frame of the pose)." + }, + "linear_speed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `twist.twist.linear`, m/s." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when a value is missing; else `null`." + }, + "position_x": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.x`, m." + }, + "position_y": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.y`, m." + }, + "position_z": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.z`, m." + }, + "summary_type": { + "const": "odometry", + "type": "string" + }, + "yaw": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Heading from the pose quaternion, radians." + } + }, + "required": [ + "summary_type", + "frame_id", + "child_frame_id", + "linear_speed", + "angular_z", + "position_x", + "position_y", + "position_z", + "yaw" + ], + "type": "object" +} - added
Output schema / $defs / PointCloud2SummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "fields": { + "description": "Point layout.", + "items": { + "$ref": "#/$defs/PointFieldSummary" + }, + "type": "array" + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the cloud." + }, + "height": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows (1: unorganized)." + }, + "is_dense": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "True when no point is invalid." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when partial; else `null`." + }, + "point_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "`width` times `height`." + }, + "point_step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Bytes per point." + }, + "row_step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Bytes per row." + }, + "summary_type": { + "const": "point_cloud2", + "type": "string" + }, + "width": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Points per row." + } + }, + "required": [ + "summary_type", + "frame_id", + "width", + "height", + "point_count", + "point_step", + "row_step", + "is_dense", + "fields" + ], + "type": "object" +} - added
Output schema / $defs / PointFieldSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Elements per point." + }, + "datatype": { + "description": "Element type, e.g. `FLOAT32`.", + "type": "string" + }, + "name": { + "description": "Field name, e.g. `x`.", + "type": "string" + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Byte offset in a point." + } + }, + "required": [ + "name", + "datatype", + "offset", + "count" + ], + "type": "object" +} - added
Output schema / $defs / ScanReturnAdded value: +{ + "additionalProperties": false, + "properties": { + "beam_index": { + "description": "Index in `ranges`.", + "minimum": 0, + "type": "integer" + }, + "bearing": { + "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.", + "type": "number" + }, + "range": { + "description": "Distance, meters.", + "type": "number" + } + }, + "required": [ + "range", + "bearing", + "beam_index" + ], + "type": "object" +} - added
Output schema / $defs / ScanSectorAdded value: +{ + "additionalProperties": false, + "properties": { + "beam_count": { + "description": "Beams inside the sector (0: not covered).", + "minimum": 0, + "type": "integer" + }, + "closest": { + "anyOf": [ + { + "$ref": "#/$defs/ScanReturn" + }, + { + "type": "null" + } + ], + "description": "Closest valid return in the sector; `null` with `note`." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `closest` is `null`; `null` when it is set." + } + }, + "required": [ + "beam_count", + "closest" + ], + "type": "object" +} - added
Output schema / $defs / ScanSectorsAdded value: +{ + "additionalProperties": false, + "properties": { + "front": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings within +-45 degrees of +x." + }, + "left": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings from 45 to 135 degrees." + }, + "rear": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings beyond +-135 degrees." + }, + "right": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings from -135 to -45 degrees." + } + }, + "required": [ + "front", + "left", + "right", + "rear" + ], + "type": "object" +} - added
Output schema / $defs / TopicRateAdded value: +{ + "additionalProperties": false, + "properties": { + "basis": { + "description": "Clock the rate is measured on: `received_ns` (live) or `recorded_ns` (bag).", + "enum": [ + "received_ns", + "recorded_ns" + ], + "type": "string" + }, + "interval_cv": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Interval standard deviation over mean; `null` under 2 messages." + }, + "interval_median_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Median interval; `null` under 2 messages." + }, + "max_gap_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Longest interval; `null` under 2 messages." + }, + "mean_interval_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Mean interval; `null` under 2 messages." + }, + "message_count": { + "description": "Messages the rate is computed from.", + "minimum": 0, + "type": "integer" + }, + "observed_frequency_hz": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Messages per second on `basis`, drain excluded; the rate the caller received. `null` under 2 messages." + }, + "sim_frequency_hz": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Same rate on the messages' own stamps (publisher clock); `null` without advancing stamps." + }, + "startup_burst_count": { + "description": "Leading messages set aside as a start-up drain (live only); still in `message_count`.", + "minimum": 0, + "type": "integer" + }, + "trailing_gap_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Time from the last message to the end of a live observation; else `null`." + }, + "verdict": { + "description": "First match: `silent` (none), `insufficient` (under 5), `intermittent` (an interval over 3x the median), `stable` (cv < 0.2), `jittery` (< 0.5), `erratic`. Describes this window, not a diagnosis.", + "enum": [ + "silent", + "insufficient", + "intermittent", + "stable", + "jittery", + "erratic" + ], + "type": "string" + }, + "verdict_note": { + "description": "The verdict in plain words.", + "type": "string" + }, + "window_s": { + "description": "Observation length, seconds.", + "minimum": 0, + "type": "number" + } + }, + "required": [ + "basis", + "message_count", + "startup_burst_count", + "window_s", + "mean_interval_s", + "interval_median_s", + "max_gap_s", + "interval_cv", + "observed_frequency_hz", + "sim_frequency_hz", + "trailing_gap_s", + "verdict", + "verdict_note" + ], + "type": "object" +} - changed
Output schema / descriptionPrevious value: -"Envelope returned by the `sample_messages` tool."New value: +"Samples returned by `sample_messages`, `peek_dds_samples` and `peek_bag_samples`." - changed
Output schema / properties / count / descriptionPrevious value: -"Number of samples actually returned. May be 0 (no publisher active in live mode, or empty mock fixture), less than the requested count (topic yielded fewer messages within the timeout), or capped by the the silent maximum of 50: request `count > 50` and you will receive at most 50 without warning."New value: +"Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50)." - removed
Output schema / properties / count / titleRemoved value: -"Count" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - changed
Output schema / properties / note / descriptionPrevious value: -"Why `samples` is empty or limited, when the cause is not obvious (for example payload decoding is disabled for DDS user topics). `None` when there is nothing to add."New value: +"Why `samples` is empty or limited, when not obvious; else `None`." - removed
Output schema / properties / note / titleRemoved value: -"Note" - added
Output schema / properties / rateAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/TopicRate" + }, + { + "type": "null" + } + ], + "default": null, + "description": "How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10." +} - changed
Output schema / properties / samples / descriptionPrevious value: -"The sampled messages, ordered as received from the backend."New value: +"The samples, in backend order." - removed
Output schema / properties / samples / titleRemoved value: -"Samples" - removed
Output schema / properties / topic / descriptionRemoved value: -"Topic the samples were taken from, echoed from the request." - removed
Output schema / properties / topic / titleRemoved value: -"Topic"
- Changed
sample_messages56 fields changed- changed
Input schema / properties / arrays_summary_only / descriptionPrevious value: -"When true, array fields are replaced by a short type and length summary instead of their elements. Use it to inspect the non-array fields of large messages (images, scans, point clouds). Defaults to false."New value: +"When true, arrays become a type-and-length summary: use it to read the non-array fields of large messages. Default false." - removed
Input schema / properties / arrays_summary_only / titleRemoved value: -"Arrays Summary Only" - changed
Input schema / properties / count / descriptionPrevious value: -"Number of messages to wait for. Defaults to 5; at most 50 (a larger request is capped to 50 and `note` says so). 0 returns nothing. `SampleResult.count` is how many arrived: fewer than requested when the topic publishes slowly or the deadline (`timeout_s`) ran out."New value: +"Messages to wait for, default 5, at most 50 (more is capped and `note` says so); 0 returns nothing. Fewer arrive when the topic is slow or `timeout_s` runs out." - removed
Input schema / properties / count / titleRemoved value: -"Count" - changed
Input schema / properties / max_array_length / descriptionPrevious value: -"Longest array, string or bytes value to return in full, 1..65536; longer ones are cut after their first N elements or characters and the field's dotted path is listed in the sample's `_truncated_fields`. Defaults to 128, the `ros2 topic echo` default, which cuts a 541-beam `LaserScan` after 128 ranges. Pass null to return everything in full (large for images and point clouds; a message over the server's size cap, 1 MiB by default, is dropped with a note, and a very large message may not print before the deadline)."New value: +"Longest array, string or bytes value returned in full, 1..65536, default 128 (a 541-beam `LaserScan` is cut to 128 ranges; its `summary` still covers all). Longer values are cut and listed in `_truncated_fields`. Null returns everything (large for images and clouds; a message over 1 MiB is dropped with a note)." - removed
Input schema / properties / max_array_length / titleRemoved value: -"Max Array Length" - changed
Input schema / properties / timeout_s / descriptionPrevious value: -"Seconds to wait for the messages, 1..45, default 10. Bounds the whole call, topic lookup and the `ros2` CLI start-up (a few seconds on a slow machine) included: the call returns within about `timeout_s` + 2 s. Whatever arrived by then is returned with a `note`. Raise it for a topic that publishes slower than 1 Hz. The server handles one call at a time, so a long wait delays other tool calls."New value: +"Seconds to wait, 1..40, default 10, covering topic lookup and CLI start-up. The call returns within `timeout_s` plus a few seconds, with what arrived and a `note`. Raise it for a topic slower than 1 Hz." - changed
Input schema / properties / timeout_s / maximumPrevious value: -45New value: +40 - removed
Input schema / properties / timeout_s / titleRemoved value: -"Timeout S" - changed
Input schema / properties / topic / descriptionPrevious value: -"Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and underscores; everything else (whitespace, quotes, shell metacharacters, `//`, trailing `/`) is rejected before reaching the `ros2` CLI."New value: +"Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - removed
Input schema / titleRemoved value: -"sample_messagesArguments" - added
Output schema / $defs / ImageSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "data_length": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixel buffer size in bytes (see `data_length_basis`)." + }, + "data_length_basis": { + "anyOf": [ + { + "enum": [ + "payload", + "step_x_height" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)." + }, + "encoding": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Pixel encoding, e.g. `rgb8`." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the camera." + }, + "height": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixels." + }, + "is_bigendian": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Big-endian pixels." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when partial; else `null`." + }, + "step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Row length, bytes." + }, + "summary_type": { + "const": "image", + "type": "string" + }, + "width": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Pixels." + } + }, + "required": [ + "summary_type", + "frame_id", + "width", + "height", + "encoding", + "step", + "is_bigendian", + "data_length", + "data_length_basis" + ], + "type": "object" +} - added
Output schema / $defs / ImuSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angular_velocity_norm": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `angular_velocity`, rad/s." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the IMU." + }, + "linear_acceleration_norm": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `linear_acceleration`, m/s^2." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when a value is unusable; else `null`." + }, + "orientation_valid": { + "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.", + "type": "boolean" + }, + "pitch": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about y, radians." + }, + "roll": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about x, radians." + }, + "summary_type": { + "const": "imu", + "type": "string" + }, + "yaw": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Rotation about z, radians." + } + }, + "required": [ + "summary_type", + "frame_id", + "orientation_valid", + "roll", + "pitch", + "yaw", + "angular_velocity_norm", + "linear_acceleration_norm" + ], + "type": "object" +} - added
Output schema / $defs / LaserScanSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angle_increment": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Angle between beams, rad." + }, + "angle_max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Bearing of the last beam, rad." + }, + "angle_min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Bearing of beam 0, rad." + }, + "beam_count": { + "description": "Beams in `ranges`.", + "minimum": 0, + "type": "integer" + }, + "closest_obstacle": { + "anyOf": [ + { + "$ref": "#/$defs/ScanReturn" + }, + { + "type": "null" + } + ], + "description": "Closest valid return of the scan; `null` with `note`." + }, + "finite_count": { + "description": "Beams with a finite range.", + "minimum": 0, + "type": "integer" + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame." + }, + "inf_count": { + "description": "Beams at `+inf`.", + "minimum": 0, + "type": "integer" + }, + "nan_count": { + "description": "Beams at `nan`.", + "minimum": 0, + "type": "integer" + }, + "neg_inf_count": { + "description": "Beams at `-inf`.", + "minimum": 0, + "type": "integer" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when the summary is partial; else `null`." + }, + "out_of_range_count": { + "description": "Finite ranges outside `range_min`..`range_max` (not valid).", + "minimum": 0, + "type": "integer" + }, + "range_max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Largest valid range, m." + }, + "range_min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Smallest valid range, m." + }, + "sectors": { + "$ref": "#/$defs/ScanSectors", + "description": "Closest valid return per sector." + }, + "summary_type": { + "const": "laser_scan", + "type": "string" + } + }, + "required": [ + "summary_type", + "frame_id", + "beam_count", + "angle_min", + "angle_max", + "angle_increment", + "range_min", + "range_max", + "finite_count", + "inf_count", + "neg_inf_count", + "nan_count", + "out_of_range_count", + "closest_obstacle", + "sectors" + ], + "type": "object" +} - removed
Output schema / $defs / MessageSample / descriptionRemoved value: -"A single sampled message on a topic." - removed
Output schema / $defs / MessageSample / properties / message_type / descriptionRemoved value: -"ROS2 message type of this sample, e.g. `geometry_msgs/msg/Twist`." - removed
Output schema / $defs / MessageSample / properties / message_type / titleRemoved value: -"Message Type" - changed
Output schema / $defs / MessageSample / properties / payload / descriptionPrevious value: -"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded."New value: +"Message fields as nested values; `nan`/`inf` are strings. Live `sample_messages` adds `_truncated_fields` (cut paths) and `_raw_text` (unparsable output)." - removed
Output schema / $defs / MessageSample / properties / payload / titleRemoved value: -"Payload" - changed
Output schema / $defs / MessageSample / properties / received_ns / descriptionPrevious value: -"Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise."New value: +"Wall-clock ns when the `ros2` CLI printed the message (live `sample_messages` only)." - removed
Output schema / $defs / MessageSample / properties / received_ns / titleRemoved value: -"Received Ns" - changed
Output schema / $defs / MessageSample / properties / recorded_ns / descriptionPrevious value: -"Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role."New value: +"Bag record time in ns (`peek_bag_samples` only)." - removed
Output schema / $defs / MessageSample / properties / recorded_ns / titleRemoved value: -"Recorded Ns" - removed
Output schema / $defs / MessageSample / properties / stamp_source / anyOfRemoved value: -[ - { - "enum": [ - "header", - "none", - "recorded" - ], - "type": "string" - }, - { - "type": "null" - } -] - removed
Output schema / $defs / MessageSample / properties / stamp_source / defaultRemoved value: -null - changed
Output schema / $defs / MessageSample / properties / stamp_source / descriptionPrevious value: -"Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say."New value: +"Origin of `timestamp_ns`: `header`, `payload`, `recorded` (bag record time), `dds_source` or `none` (0)." - added
Output schema / $defs / MessageSample / properties / stamp_source / enumAdded value: +[ + "header", + "payload", + "recorded", + "dds_source", + "none" +] - removed
Output schema / $defs / MessageSample / properties / stamp_source / titleRemoved value: -"Stamp Source" - added
Output schema / $defs / MessageSample / properties / stamp_source / typeAdded value: +"string" - added
Output schema / $defs / MessageSample / properties / summaryAdded value: +{ + "anyOf": [ + { + "discriminator": { + "mapping": { + "image": "#/$defs/ImageSummary", + "imu": "#/$defs/ImuSummary", + "laser_scan": "#/$defs/LaserScanSummary", + "odometry": "#/$defs/OdometrySummary", + "point_cloud2": "#/$defs/PointCloud2Summary" + }, + "propertyName": "summary_type" + }, + "oneOf": [ + { + "$ref": "#/$defs/LaserScanSummary" + }, + { + "$ref": "#/$defs/OdometrySummary" + }, + { + "$ref": "#/$defs/ImuSummary" + }, + { + "$ref": "#/$defs/ImageSummary" + }, + { + "$ref": "#/$defs/PointCloud2Summary" + } + ] + }, + { + "type": "null" + } + ], + "default": null, + "description": "Condensed reading of the whole message (see `summary_type`); `null` for other types and DDS peeks." +} - changed
Output schema / $defs / MessageSample / properties / timestamp_ns / descriptionPrevious value: -"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."New value: +"Publisher-clock ns: `header.stamp`, else the time in the body of `Clock`, `TFMessage` or `Log`, else 0 (see `stamp_source`); not the arrival time." - removed
Output schema / $defs / MessageSample / properties / timestamp_ns / titleRemoved value: -"Timestamp Ns" - removed
Output schema / $defs / MessageSample / properties / topic / descriptionRemoved value: -"Fully qualified topic name the sample was taken from, e.g. `/cmd_vel`." - removed
Output schema / $defs / MessageSample / properties / topic / titleRemoved value: -"Topic" - changed
Output schema / $defs / MessageSample / requiredPrevious value: -[ - "topic", - "message_type", - "timestamp_ns" -]New value: +[ + "topic", + "message_type", + "timestamp_ns", + "stamp_source" +] - removed
Output schema / $defs / MessageSample / titleRemoved value: -"MessageSample" - added
Output schema / $defs / OdometrySummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "angular_z": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`twist.twist.angular.z`, rad/s." + }, + "child_frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`child_frame_id` (frame of the twist)." + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` (frame of the pose)." + }, + "linear_speed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Norm of `twist.twist.linear`, m/s." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when a value is missing; else `null`." + }, + "position_x": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.x`, m." + }, + "position_y": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.y`, m." + }, + "position_z": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "`pose.pose.position.z`, m." + }, + "summary_type": { + "const": "odometry", + "type": "string" + }, + "yaw": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Heading from the pose quaternion, radians." + } + }, + "required": [ + "summary_type", + "frame_id", + "child_frame_id", + "linear_speed", + "angular_z", + "position_x", + "position_y", + "position_z", + "yaw" + ], + "type": "object" +} - added
Output schema / $defs / PointCloud2SummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "fields": { + "description": "Point layout.", + "items": { + "$ref": "#/$defs/PointFieldSummary" + }, + "type": "array" + }, + "frame_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "`header.frame_id` of the cloud." + }, + "height": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Rows (1: unorganized)." + }, + "is_dense": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "True when no point is invalid." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence when partial; else `null`." + }, + "point_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "`width` times `height`." + }, + "point_step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Bytes per point." + }, + "row_step": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Bytes per row." + }, + "summary_type": { + "const": "point_cloud2", + "type": "string" + }, + "width": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Points per row." + } + }, + "required": [ + "summary_type", + "frame_id", + "width", + "height", + "point_count", + "point_step", + "row_step", + "is_dense", + "fields" + ], + "type": "object" +} - added
Output schema / $defs / PointFieldSummaryAdded value: +{ + "additionalProperties": false, + "properties": { + "count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Elements per point." + }, + "datatype": { + "description": "Element type, e.g. `FLOAT32`.", + "type": "string" + }, + "name": { + "description": "Field name, e.g. `x`.", + "type": "string" + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Byte offset in a point." + } + }, + "required": [ + "name", + "datatype", + "offset", + "count" + ], + "type": "object" +} - added
Output schema / $defs / ScanReturnAdded value: +{ + "additionalProperties": false, + "properties": { + "beam_index": { + "description": "Index in `ranges`.", + "minimum": 0, + "type": "integer" + }, + "bearing": { + "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.", + "type": "number" + }, + "range": { + "description": "Distance, meters.", + "type": "number" + } + }, + "required": [ + "range", + "bearing", + "beam_index" + ], + "type": "object" +} - added
Output schema / $defs / ScanSectorAdded value: +{ + "additionalProperties": false, + "properties": { + "beam_count": { + "description": "Beams inside the sector (0: not covered).", + "minimum": 0, + "type": "integer" + }, + "closest": { + "anyOf": [ + { + "$ref": "#/$defs/ScanReturn" + }, + { + "type": "null" + } + ], + "description": "Closest valid return in the sector; `null` with `note`." + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Why `closest` is `null`; `null` when it is set." + } + }, + "required": [ + "beam_count", + "closest" + ], + "type": "object" +} - added
Output schema / $defs / ScanSectorsAdded value: +{ + "additionalProperties": false, + "properties": { + "front": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings within +-45 degrees of +x." + }, + "left": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings from 45 to 135 degrees." + }, + "rear": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings beyond +-135 degrees." + }, + "right": { + "$ref": "#/$defs/ScanSector", + "description": "Bearings from -135 to -45 degrees." + } + }, + "required": [ + "front", + "left", + "right", + "rear" + ], + "type": "object" +} - added
Output schema / $defs / TopicRateAdded value: +{ + "additionalProperties": false, + "properties": { + "basis": { + "description": "Clock the rate is measured on: `received_ns` (live) or `recorded_ns` (bag).", + "enum": [ + "received_ns", + "recorded_ns" + ], + "type": "string" + }, + "interval_cv": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Interval standard deviation over mean; `null` under 2 messages." + }, + "interval_median_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Median interval; `null` under 2 messages." + }, + "max_gap_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Longest interval; `null` under 2 messages." + }, + "mean_interval_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Mean interval; `null` under 2 messages." + }, + "message_count": { + "description": "Messages the rate is computed from.", + "minimum": 0, + "type": "integer" + }, + "observed_frequency_hz": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Messages per second on `basis`, drain excluded; the rate the caller received. `null` under 2 messages." + }, + "sim_frequency_hz": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Same rate on the messages' own stamps (publisher clock); `null` without advancing stamps." + }, + "startup_burst_count": { + "description": "Leading messages set aside as a start-up drain (live only); still in `message_count`.", + "minimum": 0, + "type": "integer" + }, + "trailing_gap_s": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Time from the last message to the end of a live observation; else `null`." + }, + "verdict": { + "description": "First match: `silent` (none), `insufficient` (under 5), `intermittent` (an interval over 3x the median), `stable` (cv < 0.2), `jittery` (< 0.5), `erratic`. Describes this window, not a diagnosis.", + "enum": [ + "silent", + "insufficient", + "intermittent", + "stable", + "jittery", + "erratic" + ], + "type": "string" + }, + "verdict_note": { + "description": "The verdict in plain words.", + "type": "string" + }, + "window_s": { + "description": "Observation length, seconds.", + "minimum": 0, + "type": "number" + } + }, + "required": [ + "basis", + "message_count", + "startup_burst_count", + "window_s", + "mean_interval_s", + "interval_median_s", + "max_gap_s", + "interval_cv", + "observed_frequency_hz", + "sim_frequency_hz", + "trailing_gap_s", + "verdict", + "verdict_note" + ], + "type": "object" +} - changed
Output schema / descriptionPrevious value: -"Envelope returned by the `sample_messages` tool."New value: +"Samples returned by `sample_messages`, `peek_dds_samples` and `peek_bag_samples`." - changed
Output schema / properties / count / descriptionPrevious value: -"Number of samples actually returned. May be 0 (no publisher active in live mode, or empty mock fixture), less than the requested count (topic yielded fewer messages within the timeout), or capped by the the silent maximum of 50: request `count > 50` and you will receive at most 50 without warning."New value: +"Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50)." - removed
Output schema / properties / count / titleRemoved value: -"Count" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - changed
Output schema / properties / note / descriptionPrevious value: -"Why `samples` is empty or limited, when the cause is not obvious (for example payload decoding is disabled for DDS user topics). `None` when there is nothing to add."New value: +"Why `samples` is empty or limited, when not obvious; else `None`." - removed
Output schema / properties / note / titleRemoved value: -"Note" - added
Output schema / properties / rateAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/TopicRate" + }, + { + "type": "null" + } + ], + "default": null, + "description": "How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10." +} - changed
Output schema / properties / samples / descriptionPrevious value: -"The sampled messages, ordered as received from the backend."New value: +"The samples, in backend order." - removed
Output schema / properties / samples / titleRemoved value: -"Samples" - removed
Output schema / properties / topic / descriptionRemoved value: -"Topic the samples were taken from, echoed from the request." - removed
Output schema / properties / topic / titleRemoved value: -"Topic"
- Changed
topic_metrics36 fields changed- changed
Input schema / properties / domain_id / descriptionPrevious value: -"Accepted for compatibility (0..232). TopicForge observes the domain it joined at startup (TOPICFORGE_DDS_DOMAIN_ID); this argument does not switch domains, and the response `domain_id` says which one was observed."New value: +"Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value." - removed
Input schema / properties / domain_id / titleRemoved value: -"Domain Id" - changed
Input schema / properties / topic / descriptionPrevious value: -"DDS topic name. Bare DDS names such as `scan` are valid, as are ROS 2 mangled names such as `rt/scan` and the builtin discovery topics `DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`. Letters, digits, `_`, `/` and `::` are allowed; anything else is rejected."New value: +"DDS topic name: bare (`scan`), ROS 2 mangled (`rt/scan`) or a builtin discovery topic (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Letters, digits, `_`, `/` and `::` only." - removed
Input schema / properties / topic / titleRemoved value: -"Topic" - added
Input schema / properties / window_sAdded value: +{ + "default": 60, + "description": "Window in seconds, 1..3600, default 60.", + "maximum": 3600, + "minimum": 1, + "type": "integer" +} - removed
Input schema / properties / window_secondsRemoved value: -{ - "default": 60, - "description": "Window in seconds over which to compute metrics (1..3600). Defaults to 60 seconds. Smaller windows reflect more recent state; larger windows smooth transient anomalies.", - "maximum": 3600, - "minimum": 1, - "title": "Window Seconds", - "type": "integer" -} - removed
Input schema / titleRemoved value: -"topic_metricsArguments" - changed
Output schema / descriptionPrevious value: -"Temporal metrics for a single DDS topic over a recent window.\n\nBuilt from samples that pass through `peek_dds_samples`. The buffer is\nfilled on demand because neither the `cyclonedds` nor the `fastdds`\nPython binding has a reliable per-sample callback, so a burst between two\ncalls is not seen.\n\nNumeric fields are `None` (`0` for `sequence_gaps_count`) when the data is\nmissing: no samples, no source timestamps, no sequence number in the\npayload. `samples_observed=0` is a valid result, not an error."New value: +"Temporal metrics for a single DDS topic over a recent window." - added
Output schema / properties / declared_frequency_hzAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "`1 / deadline` of the shortest writer Deadline announced: the promised rate, not the observed one; `None` when none announced." +} - removed
Output schema / properties / frequency_hz_declaredRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Declared, not measured: `1 / deadline` for the shortest QoS Deadline period announced by a writer on this topic in discovery. `None` when no writer announced a finite Deadline or the topic is not announced. It is the rate the application promised, not the rate observed.", - "title": "Frequency Hz Declared" -} - removed
Output schema / properties / frequency_hz_observedRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "`samples_observed / window_seconds_actual`. `None` when fewer than 2 samples were observed (a single sample does not define a frequency).", - "title": "Frequency Hz Observed" -} - changed
Output schema / properties / latency_available / descriptionPrevious value: -"True when at least one sample in the window carried both a publish timestamp and a receive timestamp. The percentile fields are `None` when this is False."New value: +"True when a sample carried both a publish and a receive timestamp." - removed
Output schema / properties / latency_available / titleRemoved value: -"Latency Available" - changed
Output schema / properties / latency_ns_p50 / descriptionPrevious value: -"Median publish-to-receive latency in nanoseconds, computed only when the sample type exposes a publish timestamp (typically via `header.stamp` on `Header`-stamped messages). `None` when `latency_available=False`."New value: +"Median publish-to-receive latency in ns; `None` unless `latency_available`." - removed
Output schema / properties / latency_ns_p50 / titleRemoved value: -"Latency Ns P50" - removed
Output schema / properties / latency_ns_p95 / titleRemoved value: -"Latency Ns P95" - removed
Output schema / properties / latency_ns_p99 / titleRemoved value: -"Latency Ns P99" - changed
Output schema / properties / mode_effective / descriptionPrevious value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)." - removed
Output schema / properties / mode_effective / titleRemoved value: -"Mode Effective" - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One sentence of context, or `None`." +} - added
Output schema / properties / observed_frequency_hzAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "`samples_observed / window_actual_s`; `None` under 2 samples." +} - changed
Output schema / properties / samples_observed / descriptionPrevious value: -"Number of samples in the buffer matching `topic` within the window. `0` means TopicForge has not seen any sample on this topic recently: it does NOT mean the topic has no publisher, only that no `peek_dds_samples` call captured one in the window."New value: +"Samples buffered in the window. `0` does NOT mean no publisher, only that no `peek_dds_samples` call captured one." - removed
Output schema / properties / samples_observed / titleRemoved value: -"Samples Observed" - changed
Output schema / properties / sequence_gaps_count / descriptionPrevious value: -"Number of missing sequence numbers detected in the buffered samples. `0` either means no gaps observed OR the sample type did not expose a sequence number (check `sequence_numbers_available` to disambiguate)."New value: +"Missing sequence numbers in the buffered samples; `0` also when none exist (see `sequence_numbers_available`)." - removed
Output schema / properties / sequence_gaps_count / titleRemoved value: -"Sequence Gaps Count" - changed
Output schema / properties / sequence_numbers_available / descriptionPrevious value: -"True when the adapter successfully extracted sequence numbers from at least one sample. Sequence number support depends on the message type: `Header`-stamped messages with a `seq` field expose it; primitives like `std_msgs/String` do not."New value: +"True when a sequence number could be read from a sample." - removed
Output schema / properties / sequence_numbers_available / titleRemoved value: -"Sequence Numbers Available" - changed
Output schema / properties / status / descriptionPrevious value: -"How to read the numbers. `unsupported_user_topic`: the topic is a user topic, whose payload is not decoded, so no metric exists and the null fields are not a measurement. `no_samples_yet`: a supported topic with nothing buffered in the window. `ok`: metrics computed from buffered samples."New value: +"`ok`; `no_samples_yet` (empty buffer); `unsupported_user_topic` (payload not decoded: null fields are not measurements)." - removed
Output schema / properties / status / titleRemoved value: -"Status" - removed
Output schema / properties / topic / descriptionRemoved value: -"Topic the metrics were computed for." - removed
Output schema / properties / topic / titleRemoved value: -"Topic" - added
Output schema / properties / window_actual_sAdded value: +{ + "description": "Seconds actually covered; shorter than `window_s` after a recent start; `0.0` without samples.", + "minimum": 0, + "type": "number" +} - added
Output schema / properties / window_sAdded value: +{ + "description": "Requested window in seconds (echoed).", + "maximum": 3600, + "minimum": 1, + "type": "integer" +} - removed
Output schema / properties / window_secondsRemoved value: -{ - "description": "Requested window in seconds (1..3600). Echoed back from the tool call so the LLM can correlate the request.", - "maximum": 3600, - "minimum": 1, - "title": "Window Seconds", - "type": "integer" -} - removed
Output schema / properties / window_seconds_actualRemoved value: -{ - "description": "Actual elapsed seconds within the window. May be smaller than `window_seconds` when the adapter buffered samples for less time than the requested window (e.g., the server just started). `0.0` when `samples_observed=0`.", - "minimum": 0, - "title": "Window Seconds Actual", - "type": "number" -} - changed
Output schema / requiredPrevious value: -[ - "topic", - "window_seconds", - "window_seconds_actual", - "samples_observed", - "mode_effective" -]New value: +[ + "topic", + "window_s", + "window_actual_s", + "samples_observed", + "mode_effective" +]
4 tool updates
v0.6.3- Changed
health_check1 field changed- added
Output schema / properties / sim_clock_publishedAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Whether `/clock` has at least one publisher on the ROS 2 graph. True suggests nodes may run on simulated time (`use_sim_time`), so `header.stamp` values are sim time, not wall time (compare with `received_ns`). It is a hint: a publisher on `/clock` does not prove a given node follows it. `None` in mock mode or when the `ros2` CLI could not answer.", + "title": "Sim Clock Published" +}
- Changed
peek_bag_samples5 fields changed- changed
Output schema / $defs / MessageSample / properties / payload / descriptionPrevious value: -"Structured message payload. **In live mode the parser exposes the message fields as positional CSV columns** keyed as `col_0`, `col_1`, ... (`header.stamp.sec`/`nanosec` are stripped out into `timestamp_ns` when detected). The raw CSV row is preserved verbatim under the reserved `_raw_text` key so clients can re-parse against the message's IDL when needed. In mock mode the payload is fully structured and `_raw_text` is absent. Large messages (e.g. images) may be summarized to keep tool output bounded."New value: +"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded." - added
Output schema / $defs / MessageSample / properties / received_nsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise.", + "title": "Received Ns" +} - added
Output schema / $defs / MessageSample / properties / recorded_nsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role.", + "title": "Recorded Ns" +} - added
Output schema / $defs / MessageSample / properties / stamp_sourceAdded value: +{ + "anyOf": [ + { + "enum": [ + "header", + "none", + "recorded" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say.", + "title": "Stamp Source" +} - changed
Output schema / $defs / MessageSample / properties / timestamp_ns / descriptionPrevious value: -"Timestamp in nanoseconds since epoch. In live mode this is the `header.stamp` of the sampled message when present: the live adapter invokes `ros2 topic echo --csv --once`, whose flattened CSV exposes `header.stamp.sec`/`nanosec` as the first two columns for any `Header`-stamped message. **Headerless message types** (e.g. `std_msgs/String`, `geometry_msgs/Twist`) carry no embedded timestamp, and `timestamp_ns` falls back to 0; an rclpy-backed adapter will eventually expose rmw receive times for those. Mock mode emits monotonically increasing values for deterministic ordering."New value: +"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."
- Changed
peek_dds_samples5 fields changed- changed
Output schema / $defs / MessageSample / properties / payload / descriptionPrevious value: -"Structured message payload. **In live mode the parser exposes the message fields as positional CSV columns** keyed as `col_0`, `col_1`, ... (`header.stamp.sec`/`nanosec` are stripped out into `timestamp_ns` when detected). The raw CSV row is preserved verbatim under the reserved `_raw_text` key so clients can re-parse against the message's IDL when needed. In mock mode the payload is fully structured and `_raw_text` is absent. Large messages (e.g. images) may be summarized to keep tool output bounded."New value: +"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded." - added
Output schema / $defs / MessageSample / properties / received_nsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise.", + "title": "Received Ns" +} - added
Output schema / $defs / MessageSample / properties / recorded_nsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role.", + "title": "Recorded Ns" +} - added
Output schema / $defs / MessageSample / properties / stamp_sourceAdded value: +{ + "anyOf": [ + { + "enum": [ + "header", + "none", + "recorded" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say.", + "title": "Stamp Source" +} - changed
Output schema / $defs / MessageSample / properties / timestamp_ns / descriptionPrevious value: -"Timestamp in nanoseconds since epoch. In live mode this is the `header.stamp` of the sampled message when present: the live adapter invokes `ros2 topic echo --csv --once`, whose flattened CSV exposes `header.stamp.sec`/`nanosec` as the first two columns for any `Header`-stamped message. **Headerless message types** (e.g. `std_msgs/String`, `geometry_msgs/Twist`) carry no embedded timestamp, and `timestamp_ns` falls back to 0; an rclpy-backed adapter will eventually expose rmw receive times for those. Mock mode emits monotonically increasing values for deterministic ordering."New value: +"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."
- Changed
sample_messages9 fields changed- changed
Input schema / properties / count / descriptionPrevious value: -"Maximum number of recent messages to return. Defaults to 5; silently clamped to 50 (the hard cap that keeps tool output bounded; read it from `health_check.max_sample_count`). Negative values raise an error. The returned `SampleResult.count` reflects the actual number of samples produced: it can be lower than the request (empty topic, timeout, mock fixture shorter than requested)."New value: +"Number of messages to wait for. Defaults to 5; at most 50 (a larger request is capped to 50 and `note` says so). 0 returns nothing. `SampleResult.count` is how many arrived: fewer than requested when the topic publishes slowly or the deadline (`timeout_s`) ran out." - added
Input schema / properties / count / maximumAdded value: +50 - changed
Input schema / properties / max_array_length / descriptionPrevious value: -"Longest array, string or bytes value to return in full, 1..65536; longer ones are cut. A cut array is listed in the sample's `_truncated_after_columns` (index of the last kept column); a cut string or bytes value is kept as its first N characters plus `...` and listed in `_truncated_columns`. Defaults to 128, the `ros2 topic echo` default, which cuts a 541-beam `LaserScan` after 128 ranges. Pass null to return everything in full (large for images and point clouds; a message over the server's size cap, 1 MiB by default, is dropped with a note, and a very large message may not print within the echo timeout)."New value: +"Longest array, string or bytes value to return in full, 1..65536; longer ones are cut after their first N elements or characters and the field's dotted path is listed in the sample's `_truncated_fields`. Defaults to 128, the `ros2 topic echo` default, which cuts a 541-beam `LaserScan` after 128 ranges. Pass null to return everything in full (large for images and point clouds; a message over the server's size cap, 1 MiB by default, is dropped with a note, and a very large message may not print before the deadline)." - added
Input schema / properties / timeout_sAdded value: +{ + "default": 10, + "description": "Seconds to wait for the messages, 1..45, default 10. Bounds the whole call, topic lookup and the `ros2` CLI start-up (a few seconds on a slow machine) included: the call returns within about `timeout_s` + 2 s. Whatever arrived by then is returned with a `note`. Raise it for a topic that publishes slower than 1 Hz. The server handles one call at a time, so a long wait delays other tool calls.", + "maximum": 45, + "minimum": 1, + "title": "Timeout S", + "type": "number" +} - changed
Output schema / $defs / MessageSample / properties / payload / descriptionPrevious value: -"Structured message payload. **In live mode the parser exposes the message fields as positional CSV columns** keyed as `col_0`, `col_1`, ... (`header.stamp.sec`/`nanosec` are stripped out into `timestamp_ns` when detected). The raw CSV row is preserved verbatim under the reserved `_raw_text` key so clients can re-parse against the message's IDL when needed. In mock mode the payload is fully structured and `_raw_text` is absent. Large messages (e.g. images) may be summarized to keep tool output bounded."New value: +"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded." - added
Output schema / $defs / MessageSample / properties / received_nsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise.", + "title": "Received Ns" +} - added
Output schema / $defs / MessageSample / properties / recorded_nsAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role.", + "title": "Recorded Ns" +} - added
Output schema / $defs / MessageSample / properties / stamp_sourceAdded value: +{ + "anyOf": [ + { + "enum": [ + "header", + "none", + "recorded" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say.", + "title": "Stamp Source" +} - changed
Output schema / $defs / MessageSample / properties / timestamp_ns / descriptionPrevious value: -"Timestamp in nanoseconds since epoch. In live mode this is the `header.stamp` of the sampled message when present: the live adapter invokes `ros2 topic echo --csv --once`, whose flattened CSV exposes `header.stamp.sec`/`nanosec` as the first two columns for any `Header`-stamped message. **Headerless message types** (e.g. `std_msgs/String`, `geometry_msgs/Twist`) carry no embedded timestamp, and `timestamp_ns` falls back to 0; an rclpy-backed adapter will eventually expose rmw receive times for those. Mock mode emits monotonically increasing values for deterministic ordering."New value: +"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."
12 tool updates
v0.5.6- First observed
analyze_bag - First observed
detect_qos_mismatches - First observed
get_topic_info - First observed
health_check - First observed
list_endpoints - First observed
list_participants - First observed
list_topics - First observed
participant_events - First observed
peek_bag_samples - First observed
peek_dds_samples - First observed
sample_messages - First observed
topic_metrics
TDQS
Scored across 16 tools
Tool purposes are clearly separated by resource and backend: ROS 2 graph tools, DDS-layer tools, and bag-analysis tools each have distinct scopes. The main near-overlaps (peek_* and sample_* tools) are explicitly differentiated by live ROS 2, live DDS, or offline bag source, and the descriptions consistently point to the correct alternative.
All tool names use snake_case and are readable, but the set mixes verb-first names such as get_topic_info, list_topics, and analyze_bag with noun-phrase names such as health_check, graph_diff, participant_events, and topic_metrics. This is a minor inconsistency rather than a readability problem.
The 16 tools are slightly above the typical 3-15 range, but the breadth is justified by the server covering ROS 2 graph inspection, DDS discovery/QoS, bag analysis, TF diagnostics, health, and graph diffing. Each tool maps to a recognizable diagnostic job and the set does not feel bloated.
Core observability lifecycle coverage is strong: graph and endpoint discovery, live and offline sampling, bag summaries, QoS mismatch analysis, TF inspection, metrics, participant events, and health. Minor gaps remain around global service/action discovery, which is only exposed through get_node_info, and richer bag comparison, but there are no obvious dead ends for topic-level debugging.
Maintenance
Related MCP Connectors
Read-only MCP access to a documented IT fleet: state, changes, posture. 15 tools.
Read-only MCP tools for AI agent discovery, structured resources, and NIULAI information.
Read-only search over 1,706 AI agent tools and 239 MCP servers. No sign-up needed.
Read-only MCP server for the OPERANT AI operating-agent calibration benchmark.
Related MCP Servers
- AlicenseAqualityDmaintenanceRead-only Modbus TCP monitoring server that exposes safe MCP tools for AI agents to read holding/input registers, coils, and device identity from industrial devices without write access.4Apache 2.0
- AlicenseNot gradedqualityCmaintenanceMCP server for AI agents to inspect and control ROS2 graphs, enabling topic listing, node info, service calls, parameter management, and pub/echo without manual ROS2 CLI usage.12MIT
- AlicenseNot gradedqualityCmaintenanceRead-only MCP server that exposes Autopsy digital forensics case data as tools, enabling LLM clients like Cline to browse filesystems, query artifacts, and search keywords without modifying the case.1MIT
- AlicenseNot gradedqualityCmaintenanceA read-only MCP server for bounded semantic inspection of ROS 2 perception systems, enabling discovery and metadata extraction from sensors such as cameras, depth sensors, and LiDARs without device configuration or actuation.Apache 2.0