rosotacom-dev 2.5.dev51__tar.gz → 2.5.dev52__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/nightly-e2e.yml +1 -0
  2. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/PKG-INFO +8 -1
  3. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/README.md +7 -0
  4. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/README.md +3 -0
  5. rosotacom_dev-2.5.dev52/docs/findings/fastdds-datagram-cap-drops-large-samples.md +89 -0
  6. rosotacom_dev-2.5.dev52/docs/findings/graph-derived-types-block-a-non-graph-transport.md +79 -0
  7. rosotacom_dev-2.5.dev52/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +59 -0
  8. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/session-configuration.md +87 -3
  9. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/benchmark.py +12 -2
  10. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/cli.py +89 -40
  11. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/cli_benchmark.py +80 -28
  12. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +3 -0
  13. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +3 -0
  14. rosotacom_dev-2.5.dev52/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +49 -0
  15. rosotacom_dev-2.5.dev52/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +154 -0
  16. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +9 -2
  17. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +19 -7
  18. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +24 -0
  19. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +21 -3
  20. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +84 -7
  21. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/transit.py +20 -2
  22. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/PKG-INFO +8 -1
  23. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/SOURCES.txt +8 -0
  24. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/scm_file_list.json +8 -0
  25. rosotacom_dev-2.5.dev52/src/rosotacom_dev.egg-info/scm_version.json +8 -0
  26. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_markdown_links.py +15 -1
  27. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_packaged_resources.py +2 -0
  28. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_security_maintenance_config.py +4 -0
  29. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/conftest.py +1 -0
  30. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/sessions/generate_rmw_matrix.py +7 -0
  31. rosotacom_dev-2.5.dev52/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +22 -0
  32. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_benchmark.py +15 -0
  33. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_cli_benchmark.py +76 -0
  34. rosotacom_dev-2.5.dev52/tests/unit/test_ota_link_configs.py +288 -0
  35. rosotacom_dev-2.5.dev52/tests/unit/test_pub_sub_pair_types.py +144 -0
  36. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_relay_optional_parameters.py +1 -1
  37. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_transit.py +26 -2
  38. rosotacom_dev-2.5.dev51/src/rosotacom_dev.egg-info/scm_version.json +0 -8
  39. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.devcontainer/devcontainer.json.template +0 -0
  40. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  41. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/CODEOWNERS +0 -0
  42. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  43. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  44. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  45. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  46. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  47. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  48. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  49. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  50. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  51. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/actions/setup-just/action.yml +0 -0
  52. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/dependabot.yml +0 -0
  53. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/benchmark-calibrate.yml +0 -0
  54. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/benchmark-gate.yml +0 -0
  55. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/dev-release.yml +0 -0
  56. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/image-scan.yml +0 -0
  57. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/pr-lightweight.yml +0 -0
  58. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/pr-merge-gate.yml +0 -0
  59. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.github/workflows/release.yml +0 -0
  60. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/.pre-commit-config.yaml +0 -0
  61. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/AGENTS.md +0 -0
  62. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/CONCURRENCY.md +0 -0
  63. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/CONTRIBUTING.md +0 -0
  64. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/DEVELOPMENT_PRINCIPLES.md +0 -0
  65. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/LICENSE.txt +0 -0
  66. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/MANIFEST.in +0 -0
  67. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/budgets.jsonl +0 -0
  68. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/README.md +0 -0
  69. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/capacity-size-tight-cyclone.json +0 -0
  70. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  71. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-nominal-cyclone.json +0 -0
  72. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-tight-cyclone.json +0 -0
  73. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-tight-fastdds.json +0 -0
  74. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-tight-zenoh.json +0 -0
  75. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/codecov.yml +0 -0
  76. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/deployment-configuration.md +0 -0
  77. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/benchmark-ab.md +0 -0
  78. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/benchmark-bag-as-load.md +0 -0
  79. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/ci.md +0 -0
  80. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/ffmpeg-keyframes.md +0 -0
  81. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  82. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/delay-alone-no-loss.md +0 -0
  83. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/head-of-line-irregularity.md +0 -0
  84. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  85. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
  86. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/lighter-message-loss.md +0 -0
  87. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/oversubscription-queues-not-losses.md +0 -0
  88. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/reorder-becomes-reader-loss.md +0 -0
  89. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/repro/decode_chain_repro.py +0 -0
  90. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/repro/reorder_drop_repro.py +0 -0
  91. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  92. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/forensics-report.md +0 -0
  93. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/geo-link-map.md +0 -0
  94. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/github-repository-settings.md +0 -0
  95. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/link-bandwidth.md +0 -0
  96. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/link-trace.md +0 -0
  97. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/owner-runbook.md +0 -0
  98. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/performance-bands.md +0 -0
  99. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/playbooks/README.md +0 -0
  100. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/quality-model.md +0 -0
  101. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/release-notes/TEMPLATE.md +0 -0
  102. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.1.md +0 -0
  103. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.2.md +0 -0
  104. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.3.md +0 -0
  105. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.4.md +0 -0
  106. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/release.md +0 -0
  107. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  108. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0002-expectation-concepts.md +0 -0
  109. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0003-metric-backbone.md +0 -0
  110. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0004-network-profiles.md +0 -0
  111. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  112. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0006-dynamic-qos.md +0 -0
  113. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/0007-regression-gate.md +0 -0
  114. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/rfcs/README.md +0 -0
  115. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/testing.md +0 -0
  116. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/docs/work-items.md +0 -0
  117. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/install.sh +0 -0
  118. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/justfile +0 -0
  119. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/media/control_center.png +0 -0
  120. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/media/echo_test.png +0 -0
  121. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/media/forensics-report-example.png +0 -0
  122. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/media/host.png +0 -0
  123. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/media/vehicle.png +0 -0
  124. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/pyproject.toml +0 -0
  125. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/requirements.txt +0 -0
  126. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/rosotacom.yaml +0 -0
  127. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/setup.cfg +0 -0
  128. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/__init__.py +0 -0
  129. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/__main__.py +0 -0
  130. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/anonymize.py +0 -0
  131. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/bag_ground_truth.py +0 -0
  132. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/benched_set.py +0 -0
  133. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/benchmark_delta.py +0 -0
  134. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/bundle_check.py +0 -0
  135. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/deployment.py +0 -0
  136. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/ffmpeg_packet.py +0 -0
  137. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/forensics.py +0 -0
  138. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/geomap.py +0 -0
  139. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/image_cache.py +0 -0
  140. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/netem_dist.py +0 -0
  141. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/network_profiles.py +0 -0
  142. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/network_shaper.py +0 -0
  143. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/plots.py +0 -0
  144. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/py.typed +0 -0
  145. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/benched-set.yaml +0 -0
  146. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/.gitignore +0 -0
  147. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/README.md +0 -0
  148. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/__init__.py +0 -0
  149. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  150. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  151. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
  152. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  153. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  154. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  155. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  156. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  157. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  158. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  159. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  160. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  161. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  162. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  163. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  164. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  165. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  166. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  167. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  168. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  169. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  170. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  171. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  172. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  173. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  174. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  175. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  176. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  177. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  178. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  179. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  180. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  181. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  182. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  183. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  184. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  185. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  186. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  187. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  188. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  189. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  190. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  191. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  192. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  193. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  194. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  195. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  196. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  197. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  198. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  199. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  200. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  201. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  202. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  203. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  204. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  205. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  206. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  207. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  208. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  209. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  210. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  211. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  212. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  213. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  214. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  215. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  216. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  217. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  218. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  219. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  220. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  221. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  222. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ros2docker.json.example +0 -0
  223. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  224. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  225. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  226. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  227. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  228. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  229. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  230. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  231. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  232. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  233. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  234. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  235. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  236. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  237. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  238. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  239. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  240. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  241. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  242. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  243. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  244. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  245. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  246. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  247. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  248. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  249. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  250. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  251. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  252. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  253. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  254. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  255. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  256. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  257. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  258. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
  259. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  260. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  261. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  262. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  263. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  264. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  265. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  266. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  267. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  268. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  269. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  270. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  271. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  272. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  273. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  274. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  275. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  276. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  277. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
  278. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
  279. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  280. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  281. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  282. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  283. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  284. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  285. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  286. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  287. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  288. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  289. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  290. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  291. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  292. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  293. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  294. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  295. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  296. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  297. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  298. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  299. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  300. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  301. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  302. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  303. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  304. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  305. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  306. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  307. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  308. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  309. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  310. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/status_eval.py +0 -0
  311. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/stream_stats.py +0 -0
  312. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/trace_profiles.py +0 -0
  313. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom/video_quality.py +0 -0
  314. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  315. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  316. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  317. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  318. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/terminology.md +0 -0
  319. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_benched_set_registry.py +0 -0
  320. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_findings.py +0 -0
  321. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_ota_source_bundle.py +0 -0
  322. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_package_metadata.py +0 -0
  323. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  324. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_pytest_policy.py +0 -0
  325. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_readme_examples.py +0 -0
  326. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_session_configuration_doc.py +0 -0
  327. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_session_template_substitution.py +0 -0
  328. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/contract/test_workflow_contracts.py +0 -0
  329. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_anonymize_e2e.py +0 -0
  330. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_benchmark_ab.py +0 -0
  331. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_benchmark_capacity.py +0 -0
  332. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_benchmark_replay.py +0 -0
  333. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_parallel_smoke.py +0 -0
  334. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_smoke.py +0 -0
  335. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_timeline_stepping.py +0 -0
  336. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/e2e/test_video_quality_e2e.py +0 -0
  337. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  338. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  339. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  340. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  341. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  342. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_anonymize.py +0 -0
  343. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_bag_ground_truth.py +0 -0
  344. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_benched_set.py +0 -0
  345. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_benchmark_delta.py +0 -0
  346. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_bundle_check.py +0 -0
  347. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_catmux_pane_logging.py +0 -0
  348. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_cli.py +0 -0
  349. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_concurrency.py +0 -0
  350. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_container_shaping.py +0 -0
  351. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_deployment.py +0 -0
  352. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_ffmpeg_flags.py +0 -0
  353. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_ffmpeg_packet.py +0 -0
  354. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_field_profiles.py +0 -0
  355. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_forensics.py +0 -0
  356. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_geomap.py +0 -0
  357. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_image_cache.py +0 -0
  358. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_link_bytes.py +0 -0
  359. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_link_trace.py +0 -0
  360. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_netem_dist.py +0 -0
  361. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_network_profiles.py +0 -0
  362. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_network_shaper.py +0 -0
  363. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_ota_unwrapper.py +0 -0
  364. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_ota_wrapper.py +0 -0
  365. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_ota_xml.py +0 -0
  366. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_playout_pacer.py +0 -0
  367. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_plots.py +0 -0
  368. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_qos_fallback_logging.py +0 -0
  369. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_session_pipeline.py +0 -0
  370. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_stage_latency.py +0 -0
  371. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_status_eval.py +0 -0
  372. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_status_overview.py +0 -0
  373. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_status_overview_observer.py +0 -0
  374. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_stream_stats.py +0 -0
  375. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_trace_profiles.py +0 -0
  376. {rosotacom_dev-2.5.dev51 → rosotacom_dev-2.5.dev52}/tests/unit/test_video_quality.py +0 -0
@@ -108,6 +108,7 @@ jobs:
108
108
  - cyclone-local-zenoh-ros2dds-ota
109
109
  - cyclone-ota
110
110
  - fastdds
111
+ - fastdds-default
111
112
  - zen-endpoints
112
113
  steps:
113
114
  - name: Check out repository
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev51
3
+ Version: 2.5.dev52
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -580,6 +580,13 @@ the ssh client's own argv apart, and a transport that promises no more than
580
580
  `-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
581
581
  of one client, and a transport that is not ssh never agreed to understand them.
582
582
 
583
+ The same four options — `--peer-checkout`, `--peer-exec`, `--install-mode`,
584
+ `--install-pin` — are on every `ota-benchmark` genre, not just `ota-smoke`. A
585
+ measurement is the last thing that should require a shell on the peer: the
586
+ whole point of a narrower transport is that the runs which touch a machine
587
+ repeatedly can go through it, and a benchmark touches it far more often than a
588
+ smoke test does.
589
+
583
590
  ### Live status / debugging overview
584
591
 
585
592
  Enable a continuously-updated, per-topic pipeline overview by setting
@@ -535,6 +535,13 @@ the ssh client's own argv apart, and a transport that promises no more than
535
535
  `-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
536
536
  of one client, and a transport that is not ssh never agreed to understand them.
537
537
 
538
+ The same four options — `--peer-checkout`, `--peer-exec`, `--install-mode`,
539
+ `--install-pin` — are on every `ota-benchmark` genre, not just `ota-smoke`. A
540
+ measurement is the last thing that should require a shell on the peer: the
541
+ whole point of a narrower transport is that the runs which touch a machine
542
+ repeatedly can go through it, and a benchmark touches it far more often than a
543
+ smoke test does.
544
+
538
545
  ### Live status / debugging overview
539
546
 
540
547
  Enable a continuously-updated, per-topic pipeline overview by setting
@@ -28,6 +28,9 @@ living verification hook.
28
28
 
29
29
  ## Findings
30
30
 
31
+ - [Sender-side transit rows made a dead link report zero loss](sender-rows-make-a-dead-link-look-loss-free.md) - `delivered = rows - lost` counted the sender's own `sent` rows as deliveries, so a topic the receiver never saw summarised as `delivered == expected` at 0.0% loss, with the publisher's timer reported as arrival spacing.
32
+ - [A bridge that learns types from the graph cannot use a transport that does not carry the graph](graph-derived-types-block-a-non-graph-transport.md) - over `zenoh_ros2dds` the payload topic never arrived in either run: the bridge routes a topic once a local reader exists and the reader waited for the bridge, leaving only `Pending=1` in a log.
33
+ - [A Fast DDS datagram cap stops every message larger than it](fastdds-datagram-cap-drops-large-samples.md) - copying CycloneDDS's 1200 B fragment size into the Fast DDS OTA profile makes every sample above the cap never arrive, at 1200 B and at 8192 B, sync and async — while the 84 B heartbeat keeps flowing at 10 Hz.
31
34
  - [A lost keyframe silences exactly the next gop_size−2 deltas](keyframe-loss-kills-the-next-deltas.md) - libavcodec emits nothing for them, the last delta decodes with artifacts, the next keyframe restores clean decode; a lost delta never blocks decode — so the display hole is gop_size × frame period, deterministically.
32
35
  - [Oversubscription queues — it does not lose until the queue itself dies](oversubscription-queues-not-losses.md) - 16% over an emulated cap climbed one-way delay 340 → 3250 ms across twelve bins with zero loss in every one; the losses arrived only when the queue died, all at once.
33
36
  - [Best-effort reordering becomes reader-side loss, independent of history depth](reorder-becomes-reader-loss.md) - under pure delay jitter a Cyclone best-effort reader discards every overtaken sample (57% at 100 Hz under 50+-45 ms), delivery stays strictly monotonic, and depth 50 loses the same as depth 1.
@@ -0,0 +1,89 @@
1
+ # A Fast DDS Datagram Cap Stops Every Message Larger Than It
2
+
3
+ ## Claim
4
+
5
+ Setting `<maxMessageSize>` on a Fast DDS UDPv4 transport to a value below a
6
+ sample's serialized size makes that sample **never arrive** through the OTA
7
+ pipeline — not "arrive fragmented", not "arrive with loss". Messages that fit
8
+ inside the cap keep flowing at full rate on the same link, which is what makes
9
+ this dangerous: an 84 B heartbeat crossing at 10 Hz with 0.0% loss is a link
10
+ that looks alive from every direction except the one that matters.
11
+
12
+ The obvious reason to set it is symmetry with CycloneDDS, whose OTA profile
13
+ carries this link at `FragmentSize`/`MaxMessageSize` 1200 B and works. The
14
+ symmetry does not transfer: on ROS 2 Kilted with Fast DDS 3.2.4 the cap is the
15
+ single knob that breaks the link, at 1200 B and at 8192 B, with a synchronous
16
+ writer and with an asynchronous one.
17
+
18
+ ## Setup
19
+
20
+ - Host pair / topology: one host, the packaged local smoke rig — two
21
+ communication containers on their own Docker network, split ROS domains
22
+ (peer 50, peer 51, OTA 52) with the stock `domain_bridge` between them. The
23
+ same failure was first seen across two machines over a VPN; one host
24
+ reproduces it, so nothing private is needed.
25
+ - Session: a two-peer session with a `com_msgs/msg/SizedPayload` stream at
26
+ 12000 B and 10 Hz on the loaded direction plus the echo heartbeat, OTA QoS
27
+ best_effort / depth 1 / lifespan 0.7 (the deployed shape).
28
+ - rosotacom SHA: measured at `6e4b748`; re-runs record theirs.
29
+ - Profile: none for the bisect — the failure needs no impairment, which is what
30
+ makes it attributable to the configuration rather than to the link.
31
+ - Seed policy: deterministic (no netem, no random load), `n=1` per variant; the
32
+ effect is present/absent, not a distribution.
33
+
34
+ ## Evidence
35
+
36
+ Evidence grade: single-host reproduction on packaged material, one variant per
37
+ run, read from the receiving peer's own `status.txt`.
38
+
39
+ Each knob of the tuned profile was added to the working `fastdds_unicast.xml`
40
+ one at a time (2026-08-19):
41
+
42
+ | added to fastdds_unicast.xml | 12 kB stream at the receiver |
43
+ |---|---|
44
+ | interface allowlist (127.0.0.1 + OTA address) | FLOWING 10.0 Hz, 0.0% loss |
45
+ | loopback locators (default / metatraffic / initial peers) | FLOWING 10.0 Hz |
46
+ | `maxInitialPeersRange` 10 | FLOWING 10.0 Hz |
47
+ | discovery lease / announcement | FLOWING 10.0 Hz |
48
+ | socket buffer sizes (1 MB / 4 MB) | FLOWING 10.0 Hz |
49
+ | `non_blocking_send` | FLOWING 10.0 Hz |
50
+ | `publishMode` ASYNCHRONOUS | FLOWING, degraded: 9.3 Hz at 285 ms |
51
+ | **`maxMessageSize` 1200** | **IDLE — nothing delivered** |
52
+ | **`maxMessageSize` 8192** | **IDLE — nothing delivered** |
53
+ | **`maxMessageSize` 1200 + ASYNCHRONOUS** | **IDLE — nothing delivered** |
54
+
55
+ The heartbeat crossed at 10 Hz with 0.0% loss in every one of those runs,
56
+ including the three that delivered no payload at all.
57
+
58
+ Two nearby observations, both of which cost time before the bisect narrowed it:
59
+
60
+ - Two Fast DDS participants in two containers, with the capped profile and
61
+ nothing else running, DO exchange 12 kB and 38 kB samples (57–59 of 60). The
62
+ cap only breaks the full pipeline, where the sample crosses a domain bridge
63
+ and an OTA bridge, so an isolated transport probe will report the cap as
64
+ harmless.
65
+ - Asynchronous publication does not rescue it, which rules out the documented
66
+ "a synchronous writer cannot fragment" explanation as the whole story.
67
+
68
+ Verification: manual: run the packaged smoke for a session whose OTA side names
69
+ `fastdds_tuned.xml`, then again for one that adds `<maxMessageSize>1200` to it,
70
+ and read `logs/<receiver>/status/status.txt`; the payload stage is FLOWING in
71
+ the first and IDLE in the second while the heartbeat is FLOWING in both. The
72
+ shipped profiles are pinned against the cap by
73
+ `tests/unit/test_ota_link_configs.py::test_only_cyclone_caps_the_datagram_and_fast_dds_must_not`.
74
+
75
+ ## Status
76
+
77
+ confirmed, 2026-08-19.
78
+
79
+ ## Publication notes
80
+
81
+ This is why "the OTA middleware is a configuration choice" needs measuring
82
+ rather than reasoning: the two profiles that look like translations of each
83
+ other are not, and the failure mode of the wrong translation is a link that
84
+ passes a heartbeat check. Consequence for the transport comparison: Fast DDS
85
+ lets IP fragment a large sample here where CycloneDDS sends RTPS fragments, so
86
+ the two differ in what a lost packet costs and in what the tunnel has to
87
+ reassemble — the measured OpenVPN barrier on the operator's path is around
88
+ 110 kB per message. Report Fast DDS numbers with that difference stated, not as
89
+ a like-for-like transport swap.
@@ -0,0 +1,79 @@
1
+ # A Bridge That Learns Types From The Graph Cannot Use A Transport That Does Not Carry The Graph
2
+
3
+ ## Claim
4
+
5
+ `PubSubPair` resolved a stage's message type from
6
+ `get_topic_names_and_types()`, so it could only create its endpoints once
7
+ somebody else had created the matching one. On a DDS OTA link that is invisible
8
+ — the remote writer is in the same domain, so the graph knows it. Over the
9
+ zenoh DDS bridge (`rmw.ota: zenoh_ros2dds`) it is a deadlock: the bridge
10
+ creates a subscriber route for a topic only once a **local DDS reader** exists,
11
+ and the reader was waiting for the bridge to route a publisher into the graph.
12
+ Neither ever happens, the payload topic never arrives, and the only trace is one
13
+ word in a log line — `Initialized 1 pair(s). Pending=1`.
14
+
15
+ The heartbeat escapes it because the receiving peer's bridge sees the remote
16
+ liveliness token for it early enough to create the route before the reader is
17
+ needed; the wrapped payload topic does not. So the observable is again a link
18
+ that carries the small topic and silently drops the big one.
19
+
20
+ ## Setup
21
+
22
+ - Host pair / topology: two machines over a VPN, `rmw.ota: zenoh_ros2dds`
23
+ (local cyclone), split ROS domains, one 12 kB `SizedPayload` stream at 10 Hz
24
+ plus the echo heartbeat.
25
+ - rosotacom SHA: observed at `6e4b748` (before the fix), fixed in `48d84dc`.
26
+ - Profile: the drive-derived loss process; the effect is not a loss effect and
27
+ reproduced identically in both runs under it.
28
+ - Seed policy: not applicable — present/absent, `n=2` runs, both failing the
29
+ same way.
30
+
31
+ ## Evidence
32
+
33
+ Evidence grade: two live two-machine runs, read from the receiving peer's node
34
+ logs and from the zenoh bridge's own routing log.
35
+
36
+ Receiving peer, `bridge_in` (2026-08-19):
37
+
38
+ ```
39
+ [PubSubPair] Created: sub='/ota/b/heartbeat_b' ...
40
+ [PubSubPair] Created: sub='/ota/b/cam_like/ota_stamped' ...
41
+ [PubSubPair] Initialized: sub='/ota/b/heartbeat_b' type='com_msgs/msg/EchoHeartbeat'
42
+ [bridge_in] Initialized 1 pair(s). Pending=1
43
+ ```
44
+
45
+ Receiving peer, `zenoh_bridge_ros2dds`, for the whole run:
46
+
47
+ ```
48
+ routes_mgr: Route Subscriber (Zenoh:ota/b/heartbeat_b -> ROS:/ota/b/heartbeat_b) created
49
+ ```
50
+
51
+ — and no such line for `ota/b/cam_like/ota_stamped`, while the sending peer had
52
+ created its publisher route for exactly that key. Delivered payload messages in
53
+ both runs: 0 of ~810 sent.
54
+
55
+ The fix removes the dependency rather than the symptom: every session now writes
56
+ `<peer>/topic_types.yaml` (`<stage topic>: <message type>` for every stage that
57
+ peer takes part in, derived from the same pipeline description the status node
58
+ uses), the bridge and relay nodes read it, and the graph is consulted only for
59
+ what the file does not name.
60
+
61
+ Verification: `python -m pytest -q tests/unit/test_pub_sub_pair_types.py` — a
62
+ pair with a declared type creates its endpoints against an empty graph, a pair
63
+ without one still reads the graph, and a pair with neither stays pending.
64
+ `tests/unit/test_ota_link_configs.py::test_every_peer_gets_the_stage_types_its_bridges_need`
65
+ pins that the file is generated for every peer of every session.
66
+
67
+ ## Status
68
+
69
+ confirmed, 2026-08-19.
70
+
71
+ ## Publication notes
72
+
73
+ Worth stating in any "the transport is a configuration choice" claim: a
74
+ pipeline can depend on a transport property nobody wrote down — here, that the
75
+ transport propagates the ROS graph and not only the data. DDS does, the zenoh
76
+ DDS bridge does not, and the dependency only shows up as a topic that never
77
+ arrives. The general lesson is that a contract known at generation time should
78
+ be handed to the runtime rather than rediscovered from the runtime, which is
79
+ also what makes bring-up order-independent.
@@ -0,0 +1,59 @@
1
+ # Sender-Side Transit Rows Made A Dead Link Report Zero Loss
2
+
3
+ ## Claim
4
+
5
+ `summarize_transit_records` counted `delivered = rows − lost`. Sender-side
6
+ transit rows carry the status `sent` and are written whether or not anything
7
+ arrives, so a topic that the receiving peer never saw at all summarised as
8
+ `delivered == expected`, `loss_pct 0.0` — with the sender's own publication
9
+ cadence reported as the arrival spacing. The most dangerous shape a wrong number
10
+ can take: a link that delivered nothing scored better than one that delivered
11
+ 96% of its messages.
12
+
13
+ Delivery is a receiver fact. Rows still `sent` after the join are `unconfirmed`;
14
+ `loss_pct` is `None` when no receiver accounted for the topic at all; and an
15
+ oracle reads that `None` as the worst case rather than as zero.
16
+
17
+ ## Setup
18
+
19
+ - Host pair / topology: not a transport property — the summary is a pure
20
+ function of collected records, so the reproduction is a unit test over
21
+ synthetic rows. It was found on a two-machine run whose OTA transport
22
+ delivered nothing.
23
+ - rosotacom SHA: present up to `6e4b748`, fixed in `730ddf5`.
24
+ - Profile: none — the defect is in the accounting, not in the link.
25
+ - Seed policy: not applicable, deterministic.
26
+
27
+ ## Evidence
28
+
29
+ Evidence grade: unit-level, deterministic, plus the live run that exposed it.
30
+
31
+ Live (2026-08-19, `rmw.ota: zenoh_ros2dds` over two machines): the sending peer
32
+ wrote 816 `outbound`/`sent` rows, the receiving peer wrote 5 rows and no
33
+ delivery at all. The run's `result.json` reported `delivered 807, expected 807,
34
+ loss_pct 0.0` for that topic, with inter-arrival p05/p95 of 102/104 ms — the
35
+ publisher's own timer, not an arrival distribution. The receiver's `status.txt`
36
+ for the same run said `ota_recv ABSENT/GRAPH pub=0` and `com_in IDLE`.
37
+
38
+ The window mattered as much as the number: this only became reachable when
39
+ sender-side transit rows landed (issue #294). Before that, a topic with no
40
+ receiver rows had no rows at all and simply did not appear.
41
+
42
+ Verification: `python -m pytest -q tests/unit/test_transit.py -k sender_rows`
43
+ — sender rows alone report `delivered 0`, `unconfirmed 3` and `loss_pct None`,
44
+ and a mixed set counts only the receiver's verdicts.
45
+ `tests/unit/test_benchmark.py::test_an_unknown_loss_figure_is_read_as_the_worst_case`
46
+ pins that an oracle does not pass on the `None`.
47
+
48
+ ## Status
49
+
50
+ confirmed, 2026-08-19.
51
+
52
+ ## Publication notes
53
+
54
+ A measurement pipeline that gains a second observer gains a way to be
55
+ confidently wrong: the new rows were real, the join was correct, and the
56
+ aggregate stopped meaning what its name said. Worth citing whenever a loss
57
+ figure is quoted from an automated run — state which side observed it. The
58
+ general rule the fix encodes: an aggregate over rows from two observers must say
59
+ which observer each row came from before it counts anything.
@@ -167,8 +167,49 @@ not override `RMW_IMPLEMENTATION` and relies on the ambient ROS 2 defaults — n
167
167
  Per-side config blocks:
168
168
 
169
169
  - DDS implementations (`cyclone`, `fastdds`) accept:
170
- - `config: <template>` — template file under `ws/ota_configs/` (e.g. `fastdds_v1.xml`, `cyclonedds.xml`, `fastdds_easy_mode.xml`). Omit to use the RMW's built-in defaults.
171
- - `easy_mode_ip: <string>` — only honored by `fastdds_easy_mode.xml`; defaults to the first resolved peer address.
170
+ - `config: <template>` — template file under `ws/ota_configs/`
171
+ (`cyclonedds_tuned.xml`, `cyclonedds_minimal.xml`,
172
+ `cyclonedds_local_participants.xml`, `fastdds_tuned.xml`,
173
+ `fastdds_unicast.xml`, `fastdds_easy_mode.xml`).
174
+ - `easy_mode_ip: <string>` — resolved into a template's `#easy_mode_ip`
175
+ placeholder; defaults to the first resolved peer address. Only
176
+ `fastdds_easy_mode.xml` uses it.
177
+
178
+ `fastdds_easy_mode.xml` is the discovery alternative: Fast DDS 3.1 spawns or
179
+ reuses one Discovery Server per host and points the participant at the peer's
180
+ server too, so two hosts find each other with no multicast and no per-peer
181
+ locator list. It fails differently from `fastdds_tuned.xml` rather than
182
+ better — an explicit peer list cannot discover what it was not told about,
183
+ while easy mode depends on a server process being reachable on 11811 — which
184
+ is why both are packaged. Neither caps the datagram (see above); easy mode
185
+ expresses its transport settings as `<builtinTransports …>` attributes,
186
+ because replacing the transports outright would take its discovery path with
187
+ them.
188
+
189
+ **On the OTA side, omitting `config` is not "no configuration".** Each
190
+ implementation has a default template, and both defaults are the link-ready
191
+ one: `cyclone` → `cyclonedds_tuned.xml`, `fastdds` → `fastdds_tuned.xml`.
192
+ That is what makes `shared.rmw` an interchangeable choice — a session names a
193
+ middleware and gets unicast discovery with the OTA interface pinned either
194
+ way, rather than one middleware that survives a cellular tunnel and one that
195
+ has to be configured into surviving it. `fastdds_unicast.xml` is the earlier,
196
+ locator-only configuration, kept for a run that wants to measure the
197
+ difference.
198
+
199
+ The two defaults are **not** identical in what they put on the wire, and the
200
+ difference is deliberate. CycloneDDS fragments at the RTPS layer (1200 B);
201
+ Fast DDS hands a large sample to the kernel as one datagram and lets IP
202
+ fragment it. Writing the same cap into the Fast DDS profile is the obvious
203
+ move and it takes the link down: measured on the bench pair (2026-08-18/19,
204
+ ROS 2 Kilted, Fast DDS 3.2.4) a `maxMessageSize` below the sample size stops
205
+ the sample from ever arriving — at 1200 B and at 8192 B, synchronous writer
206
+ and asynchronous — while an 84 B heartbeat on the same link keeps flowing.
207
+ Plan the payload accordingly: the measured OpenVPN barrier on this path is
208
+ around 110 kB per message.
209
+
210
+ On the **local** side, omitting `config` really is no configuration: no
211
+ template is applied and no `CYCLONEDDS_URI` / `FASTDDS_DEFAULT_PROFILES_FILE`
212
+ is exported.
172
213
 
173
214
  On the **local** side of a busy machine, set
174
215
  `config: cyclonedds_local_participants.xml`. Cyclone allows 33 participants
@@ -179,10 +220,22 @@ Per-side config blocks:
179
220
  interface and sets no peer list, because local discovery must keep working.
180
221
  The OTA templates are not usable on the local side for exactly that reason.
181
222
  - `zenoh_connect_endpoints` (native, OTA side only; `zenoh` is still accepted as a legacy OTA alias) accepts:
223
+ - `transport: tcp|udp` — the inter-host transport between the two routers.
224
+ Default `tcp`. On a lossy link TCP is the wrong choice for a best-effort
225
+ stream: one retransmit stalls everything queued behind it, which turns a
226
+ cellular dip into seconds of latency instead of one lost sample. The
227
+ listening router keeps `tcp/[::]:7447` whatever this is set to, because
228
+ every rmw_zenoh node on that host is a zenoh *client* of it over
229
+ `tcp/localhost:7447`.
182
230
  - `main_peer: <peer_key>` — the peer whose zenoh router listens. Defaults to the first peer declared under `peers:`.
183
231
  - `main_port: 7447` — listening port. Default `7447`.
184
232
  - `zenoh_ros2dds` (bridge) accepts (OTA side only):
185
- - `transport: udp|tcp` — default `udp`.
233
+ - `transport: udp|tcp` — default `tcp`, measured rather than assumed. On the
234
+ bench pair (2026-08-19, the 2026-08-17 drive's loss process, 12 kB at 10 Hz)
235
+ the UDP bridge delivered in one run of five and lost 66% of that one, while
236
+ the TCP bridge delivered in every run at ~1% loss with the lowest median
237
+ latency in the campaign. UDP stays available for a link where TCP's
238
+ head-of-line blocking costs more than its retransmission buys.
186
239
  - `main_peer: <peer_key>` — defaults to the first peer.
187
240
  - `main_port: 7447` — default `7447`.
188
241
 
@@ -227,6 +280,22 @@ topics to/from zenoh. The local graph still speaks DDS, so `rmw.local` must be
227
280
  - `zen_pub_allow` / `zen_sub_allow` are derived from peer `com-name`s (`/ota/<com-name>/.*`).
228
281
  - `zen_qos_pub` is derived from per-topic `zen_qos:` entries and baked into the generated `<peer>/plugin.yaml`.
229
282
 
283
+ #### Generated per peer: `topic_types.yaml`
284
+
285
+ Every session writes `<peer>/topic_types.yaml` — `<stage topic>: <message
286
+ type>` for every stage that peer takes part in — and the bridge and relay nodes
287
+ read it. It is generated whether or not `use_status_overview` is set, and is
288
+ derived from the same pipeline description the status node uses, so the two
289
+ cannot disagree about what a stage is called.
290
+
291
+ It exists because the alternative was the ROS graph, and the graph makes an
292
+ endpoint's creation depend on somebody else having created the matching one
293
+ first. Over a transport that carries data but not the graph — `rmw.ota:
294
+ zenoh_ros2dds`, where the bridge routes a topic only once a local DDS reader
295
+ exists — that is a deadlock, and a silent one: the receiving peer logs
296
+ `Initialized 1 pair(s). Pending=1` and the topic simply never arrives. The graph
297
+ is still consulted for anything the file does not name.
298
+
230
299
  #### Domain bridging
231
300
 
232
301
  If `shared.ota_domain_id` and `peer_settings.<peer>.domain_id` are both set and
@@ -421,6 +490,21 @@ Each entry is either:
421
490
  - `expect` (optional mapping); `smoke_probe: false` keeps the topic in the
422
491
  generated contract but excludes it from synthetic local-smoke probes
423
492
 
493
+ Two `expect` keys describe the *synthetic source* rather than the contract,
494
+ for the runs (local smoke, `ota-smoke`, OTA benchmark genres) that drive a
495
+ session with generated traffic instead of a replay:
496
+
497
+ - `smoke_native_hz` — publish the native topic at this rate instead of
498
+ deriving it from the `hz` bounds. Needed wherever the pipeline changes the
499
+ rate (`drop`, `throttle_hz`, `trickle_hz`), because the asserted rate is
500
+ the delivered one and the source has to run faster.
501
+ - `smoke_native_bytes` — the payload size for a
502
+ `com_msgs/msg/SizedPayload` topic (default 66000). The size belongs to the
503
+ load a session wants driven: comparing two OTA middlewares, or two
504
+ profiles, means holding the payload at a chosen size — a 38 kB camera
505
+ keyframe, say — rather than at whatever constant the driver carries. It is
506
+ also what the receiving side asserts, so both ends stay consistent.
507
+
424
508
  In split-domain mode the generator must emit a standard ROS 2 `domain_bridge` YAML with explicit
425
509
  message types for `/com/...` topics. For that reason, user-defined topic entries need `type:`.
426
510
  The auto-generated heartbeat topic is the only exception because its type is known by the generator.
@@ -191,9 +191,19 @@ def oracle_passes(loss_pct: float, latency_ms: float | None, thresholds: OracleT
191
191
  return loss_pct <= thresholds.max_loss_pct and latency_ms <= thresholds.max_latency_ms
192
192
 
193
193
 
194
+ def _oracle_loss_pct(value: Any) -> float:
195
+ """A topic's loss for an oracle, with "unknown" read as the worst case.
196
+
197
+ `summarize_transit_records` reports `None` when no receiving peer accounted
198
+ for the topic at all. That is not zero loss; it is no evidence, and an
199
+ oracle that let it pass would turn a dead link into a green run.
200
+ """
201
+ return 100.0 if value is None else float(value)
202
+
203
+
194
204
  def oracle_passes_topic(topic_summary: dict[str, Any], thresholds: OracleThresholds) -> bool:
195
205
  """Apply the oracle to one topic of ``transit.summarize_transit_records`` output."""
196
- loss_pct = float(topic_summary.get("loss_pct", 100.0))
206
+ loss_pct = _oracle_loss_pct(topic_summary.get("loss_pct", 100.0))
197
207
  latency = (topic_summary.get("ota_hop_ms") or {}).get(thresholds.latency_quantile)
198
208
  return oracle_passes(loss_pct, None if latency is None else float(latency), thresholds)
199
209
 
@@ -323,7 +333,7 @@ def evaluate_bag_run(
323
333
  lost = data.get("lost")
324
334
  reordered = data.get("reordered")
325
335
  run_expected = data.get("expected")
326
- loss_pct = float(data.get("loss_pct", 100.0)) if data else 100.0
336
+ loss_pct = _oracle_loss_pct(data.get("loss_pct", 100.0)) if data else 100.0
327
337
  latency_p95 = (data.get("ota_hop_ms") or {}).get(quantile) if data else None
328
338
  jitter_p95 = (data.get("jitter_ms") or {}).get("p95") if data else None
329
339
  latency_val = None if latency_p95 is None else float(latency_p95)
@@ -3668,8 +3668,18 @@ def _ota_extract_peer_artifacts(instance: SessionInstance, peer: OtaSmokePeer, e
3668
3668
  extracted = archive.extractfile(member)
3669
3669
  if extracted is None:
3670
3670
  continue
3671
+ # A peer's own `.session_readonly.yaml` is written 0400 on purpose
3672
+ # ("generated, do not edit"), and a peer that runs on the orchestrator
3673
+ # collects into the very directory it wrote. Replacing the file rather
3674
+ # than opening it for writing keeps both true: the copy lands, and the
3675
+ # mode the peer chose travels with it.
3676
+ if target.exists():
3677
+ target.unlink()
3671
3678
  with extracted, target.open("wb") as handle:
3672
3679
  shutil.copyfileobj(extracted, handle)
3680
+ mode = member.mode & 0o7777
3681
+ if mode:
3682
+ target.chmod(mode)
3673
3683
  except tarfile.TarError:
3674
3684
  print(f"OTA smoke: could not unpack collected artifacts from peer {peer.name}", file=sys.stderr)
3675
3685
 
@@ -6323,6 +6333,33 @@ def _smoke_publish_rate(expect: Any) -> float:
6323
6333
  return 5.0
6324
6334
 
6325
6335
 
6336
+ #: What a `com_msgs/msg/SizedPayload` smoke stream carries when the session does
6337
+ #: not say. Big enough to fragment on any real link, which is what the packaged
6338
+ #: examples want to exercise.
6339
+ SMOKE_SIZED_PAYLOAD_BYTES = 66000
6340
+
6341
+
6342
+ def _smoke_native_payload_bytes(expect: Any, msg_type: str | None) -> int | None:
6343
+ """How large the synthetic source's `SizedPayload` messages should be.
6344
+
6345
+ The size is a property of the load a session wants driven, not of the smoke
6346
+ driver, so a session may declare `expect.smoke_native_bytes` — the size
6347
+ sibling of `smoke_native_hz`. Comparing two OTA middlewares means holding
6348
+ the payload at a chosen size (a 38 kB camera keyframe, say) rather than at
6349
+ whatever constant the driver happens to carry.
6350
+ """
6351
+ if msg_type != "com_msgs/msg/SizedPayload":
6352
+ return None
6353
+ if isinstance(expect, dict):
6354
+ declared = expect.get("smoke_native_bytes")
6355
+ if declared is not None:
6356
+ size = int(declared)
6357
+ if size <= 0:
6358
+ raise RuntimeError("expect.smoke_native_bytes must be a positive number of bytes.")
6359
+ return size
6360
+ return SMOKE_SIZED_PAYLOAD_BYTES
6361
+
6362
+
6326
6363
  def _smoke_native_publish_rate(expect: Any) -> float:
6327
6364
  """The rate the synthetic source should publish the NATIVE topic at.
6328
6365
 
@@ -6529,7 +6566,7 @@ def _received_crossed_topics(cfg: dict[str, Any], receiver_peer_key: str) -> lis
6529
6566
  hz_min=expect_hz_min,
6530
6567
  hz_max=expect_hz_max,
6531
6568
  max_delay_s=expect_max_delay_s,
6532
- expected_size=66000 if entry.msg_type == "com_msgs/msg/SizedPayload" else None,
6569
+ expected_size=_smoke_native_payload_bytes(entry.expect, entry.msg_type),
6533
6570
  publish_qos=_smoke_publish_qos(entry.qos),
6534
6571
  ),
6535
6572
  ]
@@ -6752,7 +6789,7 @@ def _smoke_topic_pub_qos_args(qos: dict[str, Any] | None) -> str:
6752
6789
  def _smoke_publisher_command(spec: SmokeTopicSpec, ros_setup: str, duration: float) -> str:
6753
6790
  assert spec.publish_topic is not None and spec.publish_type is not None
6754
6791
  if spec.publish_type == "com_msgs/msg/SizedPayload":
6755
- size = spec.expected_size or 66000
6792
+ size = spec.expected_size or SMOKE_SIZED_PAYLOAD_BYTES
6756
6793
  return (
6757
6794
  f"{ros_setup} && timeout {duration} ros2 run com_py sized_publisher --ros-args "
6758
6795
  f"-p topic:={shlex.quote(spec.publish_topic)} -p size:={size} -p rate:={spec.publish_rate}"
@@ -8550,6 +8587,55 @@ def _add_peer_arg(parser: argparse.ArgumentParser) -> None:
8550
8587
  cast(Any, action).completer = _peer_host_completer
8551
8588
 
8552
8589
 
8590
+ def _add_ota_install_args(parser: argparse.ArgumentParser) -> None:
8591
+ """How the peers get rosotacom, and how they are reached.
8592
+
8593
+ Shared by `ota-smoke` and every OTA benchmark genre. They were only on
8594
+ `ota-smoke` while the benchmarks were something an operator ran with an ssh
8595
+ key in hand; a run driven by a narrower transport needs the same four
8596
+ options wherever it starts, or the measurement is the one thing that still
8597
+ demands a shell on the peer.
8598
+ """
8599
+ parser.add_argument(
8600
+ "--peer-checkout",
8601
+ action="append",
8602
+ default=[],
8603
+ metavar="PEER=PATH",
8604
+ help=(
8605
+ "Absolute path of a peer's project checkout, for --install-mode checkout. "
8606
+ "Required for every peer in that mode; the same repository is at a different path on each machine."
8607
+ ),
8608
+ )
8609
+ parser.add_argument(
8610
+ "--peer-exec",
8611
+ action="append",
8612
+ default=[],
8613
+ metavar="PEER=COMMAND",
8614
+ help=(
8615
+ "Run this peer's commands through COMMAND instead of ssh; the script is appended as the final "
8616
+ "argument, the same contract ssh has. For --install-mode checkout only, and mutually exclusive "
8617
+ "with --peer-ssh for the same peer. Use it to put a peer behind a transport narrower than a "
8618
+ "shell, such as a forced command that accepts only this project's own commands."
8619
+ ),
8620
+ )
8621
+ parser.add_argument(
8622
+ "--install-mode",
8623
+ choices=list(OTA_INSTALL_MODES),
8624
+ default="source",
8625
+ help=(
8626
+ "How the peers get rosotacom. 'source' (default) stages and installs what you are running — "
8627
+ "right while iterating on rosotacom. 'pin' installs a published release from the index, so the "
8628
+ "peers run the artefact that ships — right when rehearsing a deployment. 'checkout' stages and "
8629
+ "installs nothing: each peer uses its own installed rosotacom and its own project checkout "
8630
+ "(see --peer-checkout), so the run tests the machines as git left them."
8631
+ ),
8632
+ )
8633
+ parser.add_argument(
8634
+ "--install-pin",
8635
+ help="Version for --install-mode pin (default: the version of the rosotacom you are running).",
8636
+ )
8637
+
8638
+
8553
8639
  def _add_peer_ssh_arg(parser: argparse.ArgumentParser) -> None:
8554
8640
  action = parser.add_argument(
8555
8641
  "--peer-ssh",
@@ -9109,28 +9195,7 @@ def main(argv: list[str] | None = None) -> int:
9109
9195
  _add_peer_arg(ota_smoke_parser)
9110
9196
  _add_peer_address_arg(ota_smoke_parser)
9111
9197
  _add_peer_ssh_arg(ota_smoke_parser)
9112
- ota_smoke_parser.add_argument(
9113
- "--peer-checkout",
9114
- action="append",
9115
- default=[],
9116
- metavar="PEER=PATH",
9117
- help=(
9118
- "Absolute path of a peer's project checkout, for --install-mode checkout. "
9119
- "Required for every peer in that mode; the same repository is at a different path on each machine."
9120
- ),
9121
- )
9122
- ota_smoke_parser.add_argument(
9123
- "--peer-exec",
9124
- action="append",
9125
- default=[],
9126
- metavar="PEER=COMMAND",
9127
- help=(
9128
- "Run this peer's commands through COMMAND instead of ssh; the script is appended as the final "
9129
- "argument, the same contract ssh has. For --install-mode checkout only, and mutually exclusive "
9130
- "with --peer-ssh for the same peer. Use it to put a peer behind a transport narrower than a "
9131
- "shell, such as a forced command that accepts only this project's own commands."
9132
- ),
9133
- )
9198
+ _add_ota_install_args(ota_smoke_parser)
9134
9199
  ota_smoke_parser.add_argument("--target-type", choices=["auto", "session", "scenario"], default="auto")
9135
9200
  ota_smoke_parser.add_argument("--interactive", action="store_true", help="Open a local control tmux UI.")
9136
9201
  ota_smoke_parser.add_argument("--stop", action="store_true", help="Stop an OTA smoke run.")
@@ -9147,22 +9212,6 @@ def main(argv: list[str] | None = None) -> int:
9147
9212
  action="store_true",
9148
9213
  help="Reuse an already installed rosotacom source tree in the workdir.",
9149
9214
  )
9150
- ota_smoke_parser.add_argument(
9151
- "--install-mode",
9152
- choices=list(OTA_INSTALL_MODES),
9153
- default="source",
9154
- help=(
9155
- "How the peers get rosotacom. 'source' (default) stages and installs what you are running — "
9156
- "right while iterating on rosotacom. 'pin' installs a published release from the index, so the "
9157
- "peers run the artefact that ships — right when rehearsing a deployment. 'checkout' stages and "
9158
- "installs nothing: each peer uses its own installed rosotacom and its own project checkout "
9159
- "(see --peer-checkout), so the run tests the machines as git left them."
9160
- ),
9161
- )
9162
- ota_smoke_parser.add_argument(
9163
- "--install-pin",
9164
- help="Version for --install-mode pin (default: the version of the rosotacom you are running).",
9165
- )
9166
9215
  ota_smoke_parser.add_argument(
9167
9216
  "--keep-workdir",
9168
9217
  action="store_true",