autonometry 0.1.0__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 (412) hide show
  1. autonometry-0.1.0/.gitignore +49 -0
  2. autonometry-0.1.0/CHANGELOG.md +173 -0
  3. autonometry-0.1.0/LICENSE +202 -0
  4. autonometry-0.1.0/PKG-INFO +177 -0
  5. autonometry-0.1.0/README.md +135 -0
  6. autonometry-0.1.0/clients/python/autonometry_client.py +179 -0
  7. autonometry-0.1.0/clients/python/station_example.py +125 -0
  8. autonometry-0.1.0/connectors/g1/devices.json +52 -0
  9. autonometry-0.1.0/connectors/g1/plan.json +6 -0
  10. autonometry-0.1.0/connectors/g1/recorder.toml +19 -0
  11. autonometry-0.1.0/connectors/g1/tasks.json +7 -0
  12. autonometry-0.1.0/connectors/lerobot/devices.json +52 -0
  13. autonometry-0.1.0/connectors/lerobot/plan.json +14 -0
  14. autonometry-0.1.0/connectors/lerobot/recorder.toml +19 -0
  15. autonometry-0.1.0/connectors/lerobot/tasks.json +13 -0
  16. autonometry-0.1.0/connectors/ros2/autonometry_ros/autonometry_ros/__init__.py +1 -0
  17. autonometry-0.1.0/connectors/ros2/autonometry_ros/autonometry_ros/autonometry_client.py +179 -0
  18. autonometry-0.1.0/connectors/ros2/autonometry_ros/autonometry_ros/observer.py +584 -0
  19. autonometry-0.1.0/connectors/ros2/autonometry_ros/autonometry_ros/station_observer.py +205 -0
  20. autonometry-0.1.0/connectors/ros2/autonometry_ros/config/observer.params.yaml +42 -0
  21. autonometry-0.1.0/connectors/ros2/autonometry_ros/config/station.params.yaml +21 -0
  22. autonometry-0.1.0/connectors/ros2/autonometry_ros/launch/observer.launch.py +52 -0
  23. autonometry-0.1.0/connectors/ros2/autonometry_ros/launch/station.launch.py +52 -0
  24. autonometry-0.1.0/connectors/ros2/autonometry_ros/package.xml +27 -0
  25. autonometry-0.1.0/connectors/ros2/autonometry_ros/resource/autonometry_ros +0 -0
  26. autonometry-0.1.0/connectors/ros2/autonometry_ros/setup.cfg +4 -0
  27. autonometry-0.1.0/connectors/ros2/autonometry_ros/setup.py +31 -0
  28. autonometry-0.1.0/connectors/ros2/ci/check_log.py +146 -0
  29. autonometry-0.1.0/connectors/ros2/ci/check_witness.py +85 -0
  30. autonometry-0.1.0/connectors/ros2/ci/fake_honest.py +179 -0
  31. autonometry-0.1.0/connectors/ros2/ci/fake_robot.py +308 -0
  32. autonometry-0.1.0/connectors/ros2/ci/run_e2e.sh +68 -0
  33. autonometry-0.1.0/connectors/ros2/ci/run_witness.sh +101 -0
  34. autonometry-0.1.0/demo/lerobot/README.md +39 -0
  35. autonometry-0.1.0/demo/lerobot/config/devices.json +30 -0
  36. autonometry-0.1.0/demo/lerobot/config/plan.json +14 -0
  37. autonometry-0.1.0/demo/lerobot/config/tasks.json +13 -0
  38. autonometry-0.1.0/demo/lerobot/run_demo.py +190 -0
  39. autonometry-0.1.0/demo/tb4-sim/README.md +94 -0
  40. autonometry-0.1.0/demo/tb4-sim/config/devices.json +173 -0
  41. autonometry-0.1.0/demo/tb4-sim/config/plan.json +6 -0
  42. autonometry-0.1.0/demo/tb4-sim/config/recorder.toml +17 -0
  43. autonometry-0.1.0/demo/tb4-sim/config/station-devices.json +23 -0
  44. autonometry-0.1.0/demo/tb4-sim/config/station-recorder.toml +15 -0
  45. autonometry-0.1.0/demo/tb4-sim/config/tasks.json +16 -0
  46. autonometry-0.1.0/demo/tb4-sim/run_demo.sh +66 -0
  47. autonometry-0.1.0/demo/tb4-sim/scenario.py +176 -0
  48. autonometry-0.1.0/pyproject.toml +124 -0
  49. autonometry-0.1.0/spec/README.md +32 -0
  50. autonometry-0.1.0/spec/v0.3/checkpoint.schema.json +38 -0
  51. autonometry-0.1.0/spec/v0.3/definitions.md +416 -0
  52. autonometry-0.1.0/spec/v0.3/device_registry.schema.json +65 -0
  53. autonometry-0.1.0/spec/v0.3/event.schema.json +243 -0
  54. autonometry-0.1.0/spec/v0.3/plan.schema.json +38 -0
  55. autonometry-0.1.0/spec/v0.3/rules.toml +149 -0
  56. autonometry-0.1.0/spec/v0.3/task_registry.schema.json +37 -0
  57. autonometry-0.1.0/src/autonometry/__init__.py +5 -0
  58. autonometry-0.1.0/src/autonometry/checks.py +197 -0
  59. autonometry-0.1.0/src/autonometry/cli.py +1787 -0
  60. autonometry-0.1.0/src/autonometry/cli_g1.py +212 -0
  61. autonometry-0.1.0/src/autonometry/cli_lerobot.py +317 -0
  62. autonometry-0.1.0/src/autonometry/connectors/__init__.py +6 -0
  63. autonometry-0.1.0/src/autonometry/connectors/autonometry_client.py +179 -0
  64. autonometry-0.1.0/src/autonometry/connectors/g1.py +1741 -0
  65. autonometry-0.1.0/src/autonometry/connectors/g1_dds.py +1162 -0
  66. autonometry-0.1.0/src/autonometry/connectors/heartbeats.py +78 -0
  67. autonometry-0.1.0/src/autonometry/connectors/lerobot.py +990 -0
  68. autonometry-0.1.0/src/autonometry/connectors/lerobot_io.py +930 -0
  69. autonometry-0.1.0/src/autonometry/connectors/names.py +37 -0
  70. autonometry-0.1.0/src/autonometry/connectors/station.py +235 -0
  71. autonometry-0.1.0/src/autonometry/connectors/tb4.py +1457 -0
  72. autonometry-0.1.0/src/autonometry/demo/__init__.py +103 -0
  73. autonometry-0.1.0/src/autonometry/demo/session/about.txt +12 -0
  74. autonometry-0.1.0/src/autonometry/demo/session/devices.json +27 -0
  75. autonometry-0.1.0/src/autonometry/demo/session/events.jsonl +765 -0
  76. autonometry-0.1.0/src/autonometry/demo/session/plan.json +3 -0
  77. autonometry-0.1.0/src/autonometry/demo/session/tasks.json +12 -0
  78. autonometry-0.1.0/src/autonometry/display.py +38 -0
  79. autonometry-0.1.0/src/autonometry/events.py +408 -0
  80. autonometry-0.1.0/src/autonometry/fileio.py +76 -0
  81. autonometry-0.1.0/src/autonometry/keys.py +134 -0
  82. autonometry-0.1.0/src/autonometry/merkle.py +87 -0
  83. autonometry-0.1.0/src/autonometry/paths.py +17 -0
  84. autonometry-0.1.0/src/autonometry/py.typed +0 -0
  85. autonometry-0.1.0/src/autonometry/reader.py +420 -0
  86. autonometry-0.1.0/src/autonometry/recorder/__init__.py +7 -0
  87. autonometry-0.1.0/src/autonometry/recorder/anchor.py +148 -0
  88. autonometry-0.1.0/src/autonometry/recorder/clock.py +111 -0
  89. autonometry-0.1.0/src/autonometry/recorder/config.py +320 -0
  90. autonometry-0.1.0/src/autonometry/recorder/engine.py +465 -0
  91. autonometry-0.1.0/src/autonometry/recorder/runtime.py +327 -0
  92. autonometry-0.1.0/src/autonometry/recorder/service.py +281 -0
  93. autonometry-0.1.0/src/autonometry/recorder/store.py +572 -0
  94. autonometry-0.1.0/src/autonometry/recorder/wire.py +144 -0
  95. autonometry-0.1.0/src/autonometry/registry.py +232 -0
  96. autonometry-0.1.0/src/autonometry/report/__init__.py +10 -0
  97. autonometry-0.1.0/src/autonometry/report/build.py +406 -0
  98. autonometry-0.1.0/src/autonometry/report/census.py +62 -0
  99. autonometry-0.1.0/src/autonometry/report/figures.py +267 -0
  100. autonometry-0.1.0/src/autonometry/report/html.py +299 -0
  101. autonometry-0.1.0/src/autonometry/report/invariants.py +212 -0
  102. autonometry-0.1.0/src/autonometry/report/statements.py +173 -0
  103. autonometry-0.1.0/src/autonometry/report/templates/report.html.j2 +309 -0
  104. autonometry-0.1.0/src/autonometry/report/trust.py +173 -0
  105. autonometry-0.1.0/src/autonometry/sealfile.py +208 -0
  106. autonometry-0.1.0/src/autonometry/sealing.py +125 -0
  107. autonometry-0.1.0/src/autonometry/signednote.py +311 -0
  108. autonometry-0.1.0/src/autonometry/spec.py +464 -0
  109. autonometry-0.1.0/src/autonometry/stats/__init__.py +12 -0
  110. autonometry-0.1.0/src/autonometry/stats/facts.py +311 -0
  111. autonometry-0.1.0/src/autonometry/stats/intervals.py +661 -0
  112. autonometry-0.1.0/src/autonometry/stats/metrics.py +955 -0
  113. autonometry-0.1.0/src/autonometry/stats/planning.py +215 -0
  114. autonometry-0.1.0/src/autonometry/stats/report.py +77 -0
  115. autonometry-0.1.0/src/autonometry/strictjson.py +125 -0
  116. autonometry-0.1.0/src/autonometry/timeline.py +1840 -0
  117. autonometry-0.1.0/src/autonometry/timestamps.py +67 -0
  118. autonometry-0.1.0/src/autonometry/tsa.py +294 -0
  119. autonometry-0.1.0/src/autonometry/verify.py +452 -0
  120. autonometry-0.1.0/src/autonometry/witness.py +829 -0
  121. autonometry-0.1.0/tests/client/test_client.py +227 -0
  122. autonometry-0.1.0/tests/client/test_station_example.py +85 -0
  123. autonometry-0.1.0/tests/dds/check_no_writer.py +22 -0
  124. autonometry-0.1.0/tests/dds/fake_arm_action_service.py +22 -0
  125. autonometry-0.1.0/tests/dds/fake_builtin_controller.py +15 -0
  126. autonometry-0.1.0/tests/dds/fake_policy.py +30 -0
  127. autonometry-0.1.0/tests/dds/fake_robot_lowstate.py +17 -0
  128. autonometry-0.1.0/tests/dds/fake_xr_teleoperate.py +14 -0
  129. autonometry-0.1.0/tests/dds/g1_fakes.py +74 -0
  130. autonometry-0.1.0/tests/dds/test_g1_loopback.py +361 -0
  131. autonometry-0.1.0/tests/demo_helpers.py +147 -0
  132. autonometry-0.1.0/tests/fixtures/lerobot_source/README.md +13 -0
  133. autonometry-0.1.0/tests/fixtures/lerobot_source/messages.json +326 -0
  134. autonometry-0.1.0/tests/fixtures/lerobot_windows/README.md +13 -0
  135. autonometry-0.1.0/tests/fixtures/lerobot_windows/cmd_redirect.log +21 -0
  136. autonometry-0.1.0/tests/fixtures/lerobot_windows/cmd_utf8.log +21 -0
  137. autonometry-0.1.0/tests/fixtures/lerobot_windows/fake_rollout.py +63 -0
  138. autonometry-0.1.0/tests/fixtures/lerobot_windows/ps51_foreach.log +0 -0
  139. autonometry-0.1.0/tests/fixtures/lerobot_windows/ps51_tee.log +0 -0
  140. autonometry-0.1.0/tests/fixtures/tsa/README.md +23 -0
  141. autonometry-0.1.0/tests/fixtures/tsa/freetsa_root.pem +45 -0
  142. autonometry-0.1.0/tests/fixtures/tsa/golden_checkpoint.tsr +0 -0
  143. autonometry-0.1.0/tests/g1_helpers.py +188 -0
  144. autonometry-0.1.0/tests/golden/README.md +46 -0
  145. autonometry-0.1.0/tests/golden/errors/duplicate_key.expected.json +40 -0
  146. autonometry-0.1.0/tests/golden/errors/duplicate_key.jsonl +5 -0
  147. autonometry-0.1.0/tests/golden/errors/duplicate_seq.expected.json +41 -0
  148. autonometry-0.1.0/tests/golden/errors/duplicate_seq.jsonl +7 -0
  149. autonometry-0.1.0/tests/golden/errors/integer_out_of_range.expected.json +46 -0
  150. autonometry-0.1.0/tests/golden/errors/integer_out_of_range.jsonl +6 -0
  151. autonometry-0.1.0/tests/golden/errors/invalid_json.expected.json +52 -0
  152. autonometry-0.1.0/tests/golden/errors/invalid_json.jsonl +7 -0
  153. autonometry-0.1.0/tests/golden/errors/invalid_timestamp.expected.json +58 -0
  154. autonometry-0.1.0/tests/golden/errors/invalid_timestamp.jsonl +8 -0
  155. autonometry-0.1.0/tests/golden/errors/invalid_unicode.expected.json +70 -0
  156. autonometry-0.1.0/tests/golden/errors/invalid_unicode.jsonl +10 -0
  157. autonometry-0.1.0/tests/golden/errors/invalid_utf8.expected.json +46 -0
  158. autonometry-0.1.0/tests/golden/errors/invalid_utf8.jsonl +6 -0
  159. autonometry-0.1.0/tests/golden/errors/line_too_long.expected.json +34 -0
  160. autonometry-0.1.0/tests/golden/errors/line_too_long.jsonl +4 -0
  161. autonometry-0.1.0/tests/golden/errors/non_finite_number.expected.json +52 -0
  162. autonometry-0.1.0/tests/golden/errors/non_finite_number.jsonl +7 -0
  163. autonometry-0.1.0/tests/golden/errors/not_an_object.expected.json +58 -0
  164. autonometry-0.1.0/tests/golden/errors/not_an_object.jsonl +8 -0
  165. autonometry-0.1.0/tests/golden/errors/schema_violation.expected.json +118 -0
  166. autonometry-0.1.0/tests/golden/errors/schema_violation.jsonl +18 -0
  167. autonometry-0.1.0/tests/golden/flags/attribution_inferred.expected.json +35 -0
  168. autonometry-0.1.0/tests/golden/flags/attribution_inferred.jsonl +6 -0
  169. autonometry-0.1.0/tests/golden/flags/clock_unsynced.expected.json +35 -0
  170. autonometry-0.1.0/tests/golden/flags/clock_unsynced.jsonl +6 -0
  171. autonometry-0.1.0/tests/golden/flags/distance_regression.expected.json +35 -0
  172. autonometry-0.1.0/tests/golden/flags/distance_regression.jsonl +5 -0
  173. autonometry-0.1.0/tests/golden/flags/event_outside_session.expected.json +42 -0
  174. autonometry-0.1.0/tests/golden/flags/event_outside_session.jsonl +6 -0
  175. autonometry-0.1.0/tests/golden/flags/missing_initial_state.expected.json +42 -0
  176. autonometry-0.1.0/tests/golden/flags/missing_initial_state.jsonl +3 -0
  177. autonometry-0.1.0/tests/golden/flags/nested_task.expected.json +35 -0
  178. autonometry-0.1.0/tests/golden/flags/nested_task.jsonl +7 -0
  179. autonometry-0.1.0/tests/golden/flags/no_session_end.expected.json +35 -0
  180. autonometry-0.1.0/tests/golden/flags/no_session_end.jsonl +2 -0
  181. autonometry-0.1.0/tests/golden/flags/no_session_start.expected.json +35 -0
  182. autonometry-0.1.0/tests/golden/flags/no_session_start.jsonl +2 -0
  183. autonometry-0.1.0/tests/golden/flags/repeated_session_marker.expected.json +35 -0
  184. autonometry-0.1.0/tests/golden/flags/repeated_session_marker.jsonl +4 -0
  185. autonometry-0.1.0/tests/golden/flags/seq_gap.expected.json +35 -0
  186. autonometry-0.1.0/tests/golden/flags/seq_gap.jsonl +4 -0
  187. autonometry-0.1.0/tests/golden/flags/task_end_without_start.expected.json +42 -0
  188. autonometry-0.1.0/tests/golden/flags/task_end_without_start.jsonl +7 -0
  189. autonometry-0.1.0/tests/golden/flags/task_not_closed.expected.json +35 -0
  190. autonometry-0.1.0/tests/golden/flags/task_not_closed.jsonl +4 -0
  191. autonometry-0.1.0/tests/golden/flags/time_gap.expected.json +42 -0
  192. autonometry-0.1.0/tests/golden/flags/time_gap.jsonl +8 -0
  193. autonometry-0.1.0/tests/golden/flags/ts_reconstructed.expected.json +35 -0
  194. autonometry-0.1.0/tests/golden/flags/ts_reconstructed.jsonl +4 -0
  195. autonometry-0.1.0/tests/golden/flags/ts_regression.expected.json +35 -0
  196. autonometry-0.1.0/tests/golden/flags/ts_regression.jsonl +4 -0
  197. autonometry-0.1.0/tests/golden/flags/undeclared_scope.expected.json +49 -0
  198. autonometry-0.1.0/tests/golden/flags/undeclared_scope.jsonl +5 -0
  199. autonometry-0.1.0/tests/golden/g1/README.md +70 -0
  200. autonometry-0.1.0/tests/golden/g1/expected.json +70 -0
  201. autonometry-0.1.0/tests/golden/g1/session.json +39 -0
  202. autonometry-0.1.0/tests/golden/lerobot/README.md +52 -0
  203. autonometry-0.1.0/tests/golden/lerobot/expected.json +73 -0
  204. autonometry-0.1.0/tests/golden/lerobot/rollout.log +38 -0
  205. autonometry-0.1.0/tests/golden/metrics/expected.json +1547 -0
  206. autonometry-0.1.0/tests/golden/metrics/logs/devices.json +41 -0
  207. autonometry-0.1.0/tests/golden/metrics/logs/plan.json +8 -0
  208. autonometry-0.1.0/tests/golden/metrics/logs/s1.jsonl +16 -0
  209. autonometry-0.1.0/tests/golden/metrics/logs/s2.jsonl +11 -0
  210. autonometry-0.1.0/tests/golden/metrics/logs/s3.jsonl +14 -0
  211. autonometry-0.1.0/tests/golden/metrics/logs/s4.jsonl +6 -0
  212. autonometry-0.1.0/tests/golden/metrics/logs/tasks.json +7 -0
  213. autonometry-0.1.0/tests/golden/registry/duplicate_device_id.expected.json +12 -0
  214. autonometry-0.1.0/tests/golden/registry/duplicate_device_id.json +34 -0
  215. autonometry-0.1.0/tests/golden/registry/duplicate_key.expected.json +11 -0
  216. autonometry-0.1.0/tests/golden/registry/duplicate_key.json +1 -0
  217. autonometry-0.1.0/tests/golden/registry/empty_match.expected.json +11 -0
  218. autonometry-0.1.0/tests/golden/registry/empty_match.json +9 -0
  219. autonometry-0.1.0/tests/golden/registry/human_without_location.expected.json +11 -0
  220. autonometry-0.1.0/tests/golden/registry/human_without_location.json +11 -0
  221. autonometry-0.1.0/tests/golden/registry/no_devices.expected.json +11 -0
  222. autonometry-0.1.0/tests/golden/registry/no_devices.json +3 -0
  223. autonometry-0.1.0/tests/golden/registry/non_printable_match.expected.json +11 -0
  224. autonometry-0.1.0/tests/golden/registry/non_printable_match.json +11 -0
  225. autonometry-0.1.0/tests/golden/registry/shadowed_device.expected.json +12 -0
  226. autonometry-0.1.0/tests/golden/registry/shadowed_device.json +47 -0
  227. autonometry-0.1.0/tests/golden/registry/turtlebot4.expected.json +9 -0
  228. autonometry-0.1.0/tests/golden/registry/turtlebot4.json +47 -0
  229. autonometry-0.1.0/tests/golden/registry/unitree_g1.expected.json +9 -0
  230. autonometry-0.1.0/tests/golden/registry/unitree_g1.json +44 -0
  231. autonometry-0.1.0/tests/golden/registry/unknown_field.expected.json +11 -0
  232. autonometry-0.1.0/tests/golden/registry/unknown_field.json +12 -0
  233. autonometry-0.1.0/tests/golden/report/sealed/anchors.jsonl +8 -0
  234. autonometry-0.1.0/tests/golden/report/sealed/devices.json +41 -0
  235. autonometry-0.1.0/tests/golden/report/sealed/golden.vkey +1 -0
  236. autonometry-0.1.0/tests/golden/report/sealed/plan.json +8 -0
  237. autonometry-0.1.0/tests/golden/report/sealed/s1.checkpoints.jsonl +2 -0
  238. autonometry-0.1.0/tests/golden/report/sealed/s1.jsonl +16 -0
  239. autonometry-0.1.0/tests/golden/report/sealed/s2.checkpoints.jsonl +2 -0
  240. autonometry-0.1.0/tests/golden/report/sealed/s2.jsonl +11 -0
  241. autonometry-0.1.0/tests/golden/report/sealed/s3.checkpoints.jsonl +2 -0
  242. autonometry-0.1.0/tests/golden/report/sealed/s3.jsonl +14 -0
  243. autonometry-0.1.0/tests/golden/report/sealed/s4.checkpoints.jsonl +2 -0
  244. autonometry-0.1.0/tests/golden/report/sealed/s4.jsonl +6 -0
  245. autonometry-0.1.0/tests/golden/report/sealed/tasks.json +7 -0
  246. autonometry-0.1.0/tests/golden/report/sealed.expected.json +2309 -0
  247. autonometry-0.1.0/tests/golden/report/unsealed.expected.json +2316 -0
  248. autonometry-0.1.0/tests/golden/sealed/README.md +11 -0
  249. autonometry-0.1.0/tests/golden/sealed/log.jsonl +7 -0
  250. autonometry-0.1.0/tests/golden/tb4/README.md +89 -0
  251. autonometry-0.1.0/tests/golden/tb4/expected.json +38 -0
  252. autonometry-0.1.0/tests/golden/tb4/observations.jsonl +331 -0
  253. autonometry-0.1.0/tests/golden/timeline/hint_as_event/devices.json +41 -0
  254. autonometry-0.1.0/tests/golden/timeline/hint_as_event/events.jsonl +13 -0
  255. autonometry-0.1.0/tests/golden/timeline/hint_as_event/expected.json +206 -0
  256. autonometry-0.1.0/tests/golden/timeline/hint_as_event/plan.json +12 -0
  257. autonometry-0.1.0/tests/golden/timeline/hint_as_event/tasks.json +10 -0
  258. autonometry-0.1.0/tests/golden/timeline/hint_as_shared/devices.json +41 -0
  259. autonometry-0.1.0/tests/golden/timeline/hint_as_shared/events.jsonl +12 -0
  260. autonometry-0.1.0/tests/golden/timeline/hint_as_shared/expected.json +221 -0
  261. autonometry-0.1.0/tests/golden/timeline/hint_as_shared/plan.json +12 -0
  262. autonometry-0.1.0/tests/golden/timeline/hint_as_shared/tasks.json +10 -0
  263. autonometry-0.1.0/tests/golden/timeline/humanoid/devices.json +41 -0
  264. autonometry-0.1.0/tests/golden/timeline/humanoid/events.jsonl +20 -0
  265. autonometry-0.1.0/tests/golden/timeline/humanoid/expected.json +349 -0
  266. autonometry-0.1.0/tests/golden/timeline/humanoid/plan.json +12 -0
  267. autonometry-0.1.0/tests/golden/timeline/humanoid/tasks.json +10 -0
  268. autonometry-0.1.0/tests/golden/timeline/person_stop/devices.json +41 -0
  269. autonometry-0.1.0/tests/golden/timeline/person_stop/events.jsonl +12 -0
  270. autonometry-0.1.0/tests/golden/timeline/person_stop/expected.json +235 -0
  271. autonometry-0.1.0/tests/golden/timeline/person_stop/plan.json +12 -0
  272. autonometry-0.1.0/tests/golden/timeline/person_stop/tasks.json +10 -0
  273. autonometry-0.1.0/tests/golden/timeline/preset/devices.json +41 -0
  274. autonometry-0.1.0/tests/golden/timeline/preset/events.jsonl +11 -0
  275. autonometry-0.1.0/tests/golden/timeline/preset/expected.json +189 -0
  276. autonometry-0.1.0/tests/golden/timeline/preset/plan.json +12 -0
  277. autonometry-0.1.0/tests/golden/timeline/preset/tasks.json +10 -0
  278. autonometry-0.1.0/tests/golden/timeline/registry_mismatch/devices.json +41 -0
  279. autonometry-0.1.0/tests/golden/timeline/registry_mismatch/events.jsonl +11 -0
  280. autonometry-0.1.0/tests/golden/timeline/registry_mismatch/expected.json +230 -0
  281. autonometry-0.1.0/tests/golden/timeline/registry_mismatch/plan.json +12 -0
  282. autonometry-0.1.0/tests/golden/timeline/registry_mismatch/tasks.json +10 -0
  283. autonometry-0.1.0/tests/golden/timeline/resets/devices.json +41 -0
  284. autonometry-0.1.0/tests/golden/timeline/resets/events.jsonl +16 -0
  285. autonometry-0.1.0/tests/golden/timeline/resets/expected.json +182 -0
  286. autonometry-0.1.0/tests/golden/timeline/resets/plan.json +12 -0
  287. autonometry-0.1.0/tests/golden/timeline/resets/tasks.json +10 -0
  288. autonometry-0.1.0/tests/golden/timeline/safety_stop_takeover/devices.json +41 -0
  289. autonometry-0.1.0/tests/golden/timeline/safety_stop_takeover/events.jsonl +17 -0
  290. autonometry-0.1.0/tests/golden/timeline/safety_stop_takeover/expected.json +246 -0
  291. autonometry-0.1.0/tests/golden/timeline/safety_stop_takeover/plan.json +12 -0
  292. autonometry-0.1.0/tests/golden/timeline/safety_stop_takeover/tasks.json +10 -0
  293. autonometry-0.1.0/tests/golden/timeline/two_humans/devices.json +41 -0
  294. autonometry-0.1.0/tests/golden/timeline/two_humans/events.jsonl +16 -0
  295. autonometry-0.1.0/tests/golden/timeline/two_humans/expected.json +247 -0
  296. autonometry-0.1.0/tests/golden/timeline/two_humans/plan.json +12 -0
  297. autonometry-0.1.0/tests/golden/timeline/two_humans/tasks.json +10 -0
  298. autonometry-0.1.0/tests/golden/timeline/undeclared_device/devices.json +41 -0
  299. autonometry-0.1.0/tests/golden/timeline/undeclared_device/events.jsonl +10 -0
  300. autonometry-0.1.0/tests/golden/timeline/undeclared_device/expected.json +200 -0
  301. autonometry-0.1.0/tests/golden/timeline/undeclared_device/plan.json +12 -0
  302. autonometry-0.1.0/tests/golden/timeline/undeclared_device/tasks.json +10 -0
  303. autonometry-0.1.0/tests/golden/timeline/unknown_gaps/devices.json +41 -0
  304. autonometry-0.1.0/tests/golden/timeline/unknown_gaps/events.jsonl +12 -0
  305. autonometry-0.1.0/tests/golden/timeline/unknown_gaps/expected.json +169 -0
  306. autonometry-0.1.0/tests/golden/timeline/unknown_gaps/plan.json +12 -0
  307. autonometry-0.1.0/tests/golden/timeline/unknown_gaps/tasks.json +10 -0
  308. autonometry-0.1.0/tests/golden/valid/crlf_bom_blank.expected.json +25 -0
  309. autonometry-0.1.0/tests/golden/valid/crlf_bom_blank.jsonl +6 -0
  310. autonometry-0.1.0/tests/golden/valid/multi_robot.expected.json +39 -0
  311. autonometry-0.1.0/tests/golden/valid/multi_robot.jsonl +30 -0
  312. autonometry-0.1.0/tests/golden/valid/support_and_clock.expected.json +25 -0
  313. autonometry-0.1.0/tests/golden/valid/support_and_clock.jsonl +10 -0
  314. autonometry-0.1.0/tests/golden/witness/README.md +24 -0
  315. autonometry-0.1.0/tests/golden/witness/anchors.jsonl +8 -0
  316. autonometry-0.1.0/tests/golden/witness/expected.json +203 -0
  317. autonometry-0.1.0/tests/golden/witness/keys.vkey +2 -0
  318. autonometry-0.1.0/tests/golden/witness/robot/devices.json +41 -0
  319. autonometry-0.1.0/tests/golden/witness/robot/plan.json +8 -0
  320. autonometry-0.1.0/tests/golden/witness/robot/r01.checkpoints.jsonl +2 -0
  321. autonometry-0.1.0/tests/golden/witness/robot/r01.jsonl +304 -0
  322. autonometry-0.1.0/tests/golden/witness/robot/r02.checkpoints.jsonl +2 -0
  323. autonometry-0.1.0/tests/golden/witness/robot/r02.jsonl +302 -0
  324. autonometry-0.1.0/tests/golden/witness/robot/tasks.json +7 -0
  325. autonometry-0.1.0/tests/golden/witness/station/devices.json +41 -0
  326. autonometry-0.1.0/tests/golden/witness/station/plan.json +8 -0
  327. autonometry-0.1.0/tests/golden/witness/station/r01.checkpoints.jsonl +2 -0
  328. autonometry-0.1.0/tests/golden/witness/station/r01.jsonl +309 -0
  329. autonometry-0.1.0/tests/golden/witness/station/r02.checkpoints.jsonl +2 -0
  330. autonometry-0.1.0/tests/golden/witness/station/r02.jsonl +309 -0
  331. autonometry-0.1.0/tests/golden/witness/station/tasks.json +7 -0
  332. autonometry-0.1.0/tests/json_helpers.py +36 -0
  333. autonometry-0.1.0/tests/lerobot_helpers.py +147 -0
  334. autonometry-0.1.0/tests/recorder_helpers.py +269 -0
  335. autonometry-0.1.0/tests/report_helpers.py +102 -0
  336. autonometry-0.1.0/tests/sealing_helpers.py +105 -0
  337. autonometry-0.1.0/tests/slow/test_memory.py +31 -0
  338. autonometry-0.1.0/tests/slow/test_recorder_perf.py +145 -0
  339. autonometry-0.1.0/tests/slow/test_seal_verify_perf.py +133 -0
  340. autonometry-0.1.0/tests/slow/test_stats_coverage.py +199 -0
  341. autonometry-0.1.0/tests/slow/test_timeline_perf.py +58 -0
  342. autonometry-0.1.0/tests/stats_helpers.py +60 -0
  343. autonometry-0.1.0/tests/tb4_helpers.py +156 -0
  344. autonometry-0.1.0/tests/test_checks.py +295 -0
  345. autonometry-0.1.0/tests/test_cli.py +289 -0
  346. autonometry-0.1.0/tests/test_cli_g1.py +255 -0
  347. autonometry-0.1.0/tests/test_cli_sealing.py +416 -0
  348. autonometry-0.1.0/tests/test_connector_g1.py +1029 -0
  349. autonometry-0.1.0/tests/test_connector_g1_dds.py +685 -0
  350. autonometry-0.1.0/tests/test_connector_g1_golden.py +251 -0
  351. autonometry-0.1.0/tests/test_connector_g1_properties.py +310 -0
  352. autonometry-0.1.0/tests/test_connector_heartbeats.py +132 -0
  353. autonometry-0.1.0/tests/test_connector_lerobot.py +540 -0
  354. autonometry-0.1.0/tests/test_connector_lerobot_golden.py +216 -0
  355. autonometry-0.1.0/tests/test_connector_lerobot_import.py +745 -0
  356. autonometry-0.1.0/tests/test_connector_lerobot_liveness.py +550 -0
  357. autonometry-0.1.0/tests/test_connector_lerobot_properties.py +159 -0
  358. autonometry-0.1.0/tests/test_connector_lerobot_source.py +58 -0
  359. autonometry-0.1.0/tests/test_connector_names.py +50 -0
  360. autonometry-0.1.0/tests/test_connector_station.py +148 -0
  361. autonometry-0.1.0/tests/test_connector_tb4.py +942 -0
  362. autonometry-0.1.0/tests/test_connector_tb4_golden.py +133 -0
  363. autonometry-0.1.0/tests/test_connector_tb4_properties.py +126 -0
  364. autonometry-0.1.0/tests/test_core_imports.py +108 -0
  365. autonometry-0.1.0/tests/test_demo.py +191 -0
  366. autonometry-0.1.0/tests/test_fileio.py +65 -0
  367. autonometry-0.1.0/tests/test_keys.py +146 -0
  368. autonometry-0.1.0/tests/test_lerobot_acceptance.py +42 -0
  369. autonometry-0.1.0/tests/test_merkle.py +76 -0
  370. autonometry-0.1.0/tests/test_metrics_cli.py +157 -0
  371. autonometry-0.1.0/tests/test_packaging.py +66 -0
  372. autonometry-0.1.0/tests/test_paths.py +103 -0
  373. autonometry-0.1.0/tests/test_reader.py +834 -0
  374. autonometry-0.1.0/tests/test_recorder_clock_anchor.py +195 -0
  375. autonometry-0.1.0/tests/test_recorder_config.py +202 -0
  376. autonometry-0.1.0/tests/test_recorder_disk.py +153 -0
  377. autonometry-0.1.0/tests/test_recorder_engine.py +522 -0
  378. autonometry-0.1.0/tests/test_recorder_runtime.py +809 -0
  379. autonometry-0.1.0/tests/test_recorder_service.py +294 -0
  380. autonometry-0.1.0/tests/test_recorder_store.py +343 -0
  381. autonometry-0.1.0/tests/test_registry.py +383 -0
  382. autonometry-0.1.0/tests/test_release_assets.py +138 -0
  383. autonometry-0.1.0/tests/test_report.py +606 -0
  384. autonometry-0.1.0/tests/test_report_cli.py +297 -0
  385. autonometry-0.1.0/tests/test_sealfile.py +94 -0
  386. autonometry-0.1.0/tests/test_sealing.py +179 -0
  387. autonometry-0.1.0/tests/test_security_connectors.py +354 -0
  388. autonometry-0.1.0/tests/test_security_network.py +126 -0
  389. autonometry-0.1.0/tests/test_security_parsers.py +165 -0
  390. autonometry-0.1.0/tests/test_security_recorder.py +291 -0
  391. autonometry-0.1.0/tests/test_security_report.py +123 -0
  392. autonometry-0.1.0/tests/test_security_verify.py +321 -0
  393. autonometry-0.1.0/tests/test_signednote.py +237 -0
  394. autonometry-0.1.0/tests/test_spec.py +381 -0
  395. autonometry-0.1.0/tests/test_stats_intervals.py +519 -0
  396. autonometry-0.1.0/tests/test_stats_metrics.py +590 -0
  397. autonometry-0.1.0/tests/test_stats_planning.py +83 -0
  398. autonometry-0.1.0/tests/test_stats_properties.py +68 -0
  399. autonometry-0.1.0/tests/test_strictjson.py +158 -0
  400. autonometry-0.1.0/tests/test_timeline.py +846 -0
  401. autonometry-0.1.0/tests/test_timeline_cli.py +222 -0
  402. autonometry-0.1.0/tests/test_timeline_properties.py +198 -0
  403. autonometry-0.1.0/tests/test_timestamps.py +91 -0
  404. autonometry-0.1.0/tests/test_tsa.py +323 -0
  405. autonometry-0.1.0/tests/test_verify.py +502 -0
  406. autonometry-0.1.0/tests/test_version.py +11 -0
  407. autonometry-0.1.0/tests/test_witness.py +442 -0
  408. autonometry-0.1.0/tests/test_witness_cli.py +235 -0
  409. autonometry-0.1.0/tests/test_witness_properties.py +115 -0
  410. autonometry-0.1.0/tests/timeline_helpers.py +139 -0
  411. autonometry-0.1.0/tests/witness_files.py +145 -0
  412. autonometry-0.1.0/tests/witness_helpers.py +107 -0
@@ -0,0 +1,49 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .hypothesis/
12
+ .coverage
13
+ .coverage.*
14
+ htmlcov/
15
+ coverage.xml
16
+
17
+ # uv: uv.lock is committed, the environment is not
18
+ .python-version
19
+
20
+ # Secrets: never commit keys
21
+ *.key
22
+ *.pem
23
+ # Public TSA root certificate fixture (not a secret; needed to verify tests/fixtures/tsa/*.tsr)
24
+ !tests/fixtures/tsa/*.pem
25
+
26
+ # Data directories and real logs (fixtures belong under tests/)
27
+ data/
28
+ *.jsonl
29
+ !tests/**/*.jsonl
30
+ # Synthetic example log in the design-phase material (generated by its make_example_log.py)
31
+ !research/**/examples/*.jsonl
32
+ # The TurtleBot 4 integration notes' worked example (section B.5)
33
+ !research/turtlebot4/*.jsonl
34
+ # The demo session shipped in the package (autonometry demo; tests/demo_helpers.py writes it)
35
+ !src/autonometry/demo/session/*.jsonl
36
+
37
+ # Editors and OS
38
+ .idea/
39
+ .vscode/
40
+ .DS_Store
41
+ Thumbs.db
42
+
43
+ # TurtleBot 4 demo and ROS 2 builds
44
+ demo/tb4-sim/run/
45
+ connectors/ros2/build/
46
+ connectors/ros2/install/
47
+ connectors/ros2/log/
48
+ # LeRobot demo downloads, logs and report
49
+ demo/lerobot/run/
@@ -0,0 +1,173 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.0] - 2026-11-15
9
+
10
+ ### Added
11
+
12
+ - Project skeleton: `src/autonometry` package, uv lockfile, hatchling build, Apache-2.0 licence.
13
+ - `autonometry.spec`: `available_versions`, `load_schema`, `load_rules` (frozen pydantic model of `rules.toml`) and `check_consistency` for the specification shipped inside the wheel as `autonometry/spec/`.
14
+ - `autonometry` command line: `--version`, `spec list`, `spec show <version>`.
15
+ - Tooling: ruff, mypy strict, pytest with coverage gate (90 %) and a `slow` marker.
16
+ - GitHub automation: CI (Python 3.12 and 3.13, x86-64 and arm64, pip-audit), release workflow with PyPI Trusted Publishing, Dependabot.
17
+ - `SECURITY.md`, `RELEASING.md`.
18
+ - CI job that installs the lowest allowed versions of the direct dependencies (`uv --resolution lowest-direct`, Python 3.12) and runs the tests, so the minimum versions in `pyproject.toml` are proven.
19
+ - `autonometry validate PATH... [--json] [--spec VERSION]`: reads JSON Lines event logs safely and reports rejected lines and structural flags. Exit status 0 when nothing was rejected (flags are warnings), 1 when any line was rejected, 2 on bad usage. Event contents are never printed.
20
+ - `autonometry.reader`: streaming reader for one or many log files. Lines are read as bytes with a length limit (a longer line is skipped without being held in memory), decoded as UTF-8 (a BOM only at the start of a file; LF and CRLF), parsed as I-JSON (RFC 7493: no duplicate keys, no NaN or Infinity, integers within ±(2^53−1), no lone surrogates or Unicode noncharacters), validated against the event schema (integer fields must be written as integers, not `5.0`), and their `ts` checked for impossible dates. Valid events are grouped by `(robot_id, session_id)` and ordered by `seq`; a repeated `seq` is rejected. Every rejected line is counted, and the first `list_max_per_code` of each code are kept, so memory grows with the events kept, never with the size or number of rejected lines.
21
+ - `autonometry.checks`: per-session loader flags `no_session_start`, `no_session_end`, `missing_initial_state`, `undeclared_scope`, `seq_gap`, `ts_regression`, `time_gap`, `nested_task`, `task_end_without_start`, `task_not_closed`, `distance_regression`, `ts_reconstructed`, `repeated_session_marker`, `event_outside_session`.
22
+ - `autonometry.events`: immutable, JSON-serialisable results (`LogCheck`, `Session`, `LineError`, `Flag`, `Counts`) and an `Event` TypedDict.
23
+ - `autonometry.strictjson` (I-JSON parsing) and `autonometry.timestamps` (RFC 3339 UTC timestamps as integer nanoseconds).
24
+ - Spec v0.3: `rules.toml` `[errors]` table (error codes for rejected lines, `max_line_bytes = 65536`, `list_max_per_code = 100`) and the **Rejected lines** paragraph in `definitions.md` section 8. `Rules` loads and checks the new table.
25
+ - Spec v0.3, stricter log format: `ts` needs 3 to 9 fraction digits and a leap second is invalid (section 3); identifiers and scope names have a fixed character set and length (`$defs/id`, `$defs/scope`, `propertyNames` on scope maps; section 8, **Identifiers**); the **JSON** paragraph of section 8 (I-JSON, integer fields written as integers); flags `repeated_session_marker` and `event_outside_session` (section 1); error code `invalid_unicode`.
26
+ - Command line option `--verbose` (`-v`): debug output and full tracebacks.
27
+ - Dependency `jsonschema-rs`: compiled Draft 2020-12 validator, about 70 times faster than `jsonschema` on the event schema, with ECMA-262 regular expressions as JSON Schema requires. It also checks the schema itself against the meta-schema, so `jsonschema` is no longer a core dependency; it stays in the dev group as the reference in a differential test, and `types-jsonschema` is dropped.
28
+ - CI: test jobs on Windows and macOS (Python 3.12), and a dependency audit of both the locked and the lowest-version sets.
29
+ - `.gitattributes`: LF line endings in the repository on every OS, so spec file hashes match everywhere.
30
+ - Spec v0.3 section 10, "Sealing and verification": hash chain, Merkle checkpoints (RFC 9162), C2SP signed notes, RFC 3161 anchors, and the trust-level L1/L2 verification rules, with evidence, gap and file codes (`malformed_checkpoint`, `orphan_timestamp`: records that cannot be tied to one session, which like rejected lines make a result not verified). Anchors are checked against the events exactly like checkpoints, and a valid anchor counts as an anchored checkpoint. `rules.toml` `[sealing]` table (`checkpoint_every_events`, `checkpoint_every_s`, `max_commit_delay_s`, `clock_tolerance_s`, `evidence_codes`, `gap_codes`, `file_codes`); `Rules.sealing` and a consistency check that flag, error, evidence, gap and file codes are each unique and never shared. New `spec/v0.3/checkpoint.schema.json` for checkpoint and timestamp records.
31
+ - `autonometry.merkle`: RFC 9162 / RFC 6962 leaf and node hashing, and an `IncrementalTree` that computes a Merkle tree hash one leaf at a time in O(log n) space.
32
+ - `autonometry.sealing`: the event hash and chain genesis of definitions.md section 8, and an incremental `Sealer` that seals a session's events in order and decides when to checkpoint (section 10.3); rebuildable after a crash by replaying already-sealed events.
33
+ - `autonometry.signednote`: C2SP signed notes and checkpoints (Ed25519 log signatures only) -- key IDs, vkey parsing and formatting, signing, fail-closed verification (an untrusted key is ignored; a trusted key's bad signature rejects the note), and checkpoint body encoding/decoding.
34
+ - `autonometry.keys`: Ed25519 signing keys as unencrypted PKCS8 PEM, generated without overwriting and without ever leaving a partial file; private key 0600, vkey 0644; folders it creates are 0700 and existing folders are never changed; loading refuses a key that is group/other-accessible or owned by another user (POSIX; skipped with a warning on Windows).
35
+ - `autonometry.tsa`: an RFC 3161 timestamp client (the only network call in the package outside a witness, never on a recorder's write path) with a 10 s socket timeout, a 30 s total deadline per attempt, a 64 KiB response limit, no redirects, a nonce check against replays and 3 attempts with 1 s/2 s backoff; a pure `check_imprint` (decodes the token and checks its SHA-256 message imprint) and `verify_token` (checks it against trusted TSA roots at the token's own `gen_time`).
36
+ - `autonometry.sealfile`: reading and writing `<name>.checkpoints.jsonl` and anchor files under the same line rules as event logs, checked against `checkpoint.schema.json`; each record keeps its file and line, and rejected lines are listed per code like the event reader's.
37
+ - `autonometry.fileio`: one atomic-write helper (temporary file, fsync, rename, fsync of the folder on POSIX) for keys, sealed logs and checkpoint files, all created 0600, and a durable append for timestamp records. Both open files in binary mode, so Windows no longer turns `
38
+ ` into `
39
+ ` in appended records.
40
+ - `autonometry.verify`: `verify_log`, the definitions.md section 10.6 verifier. It recomputes every event's hash from its content, sealed or not, so a root exists at every size, and runs every signed checkpoint, from checkpoint and anchor files alike, through one check: a size beyond the events present is `missing_events` (including a session absent from the log), a differing root `root_mismatch`. Chain rules apply only to sessions with a sealed event. Each session gets `verified` / `partial` / `unverified` / `failed` with its evidence and gaps (per code, up to five details and a count); the result also lists rejected lines of every file read and file problems, and says whether it is verified at L1 or L2 and why not.
41
+ - Command line: `autonometry keys generate --name NAME --out PATH`; `autonometry seal IN.jsonl --key PATH --out OUT.jsonl`; `autonometry timestamp FILE.checkpoints.jsonl --tsa-url URL --tsa-root PEM`; `autonometry verify LOG.jsonl... [--vkey FILE]... [--tsa-root PEM]... [--anchor FILE]... [--require L1|L2] [--json]`. The text output ends with `result: verified` or `result: NOT verified: <reasons>` and lists rejected lines and file problems grouped by code; the JSON output carries `verified`, `reasons`, every listed rejected line and `file_problems`.
42
+ - CI: a job (Ubuntu 24.04, Python 3.12) runs the slow checks, `uv run pytest -m slow --no-cov`.
43
+ - Golden fixtures under `tests/golden/sealed/` and a real RFC 3161 response recorded once from FreeTSA under `tests/fixtures/tsa/`, covering the sealing attacks of `research/demo-prototypes/seal.py`.
44
+ - Spec v0.3, the rules for the cases the TurtleBot 4 and Unitree G1 research found uncovered (definitions.md "Changes in v0.3"): a `safety_stop` a person triggered and a `scripted` preset a person started are interventions; `actor_id` on `safety_stop` names who triggered it; damping modes are `safety_stop` only when a safety function or an emergency-stop control caused them; pre-recorded references run by a controller are `scripted`; the device registry declares every commanding source; new sections 6.11 to 6.15 (continuous goal input, several sources at once, presets started by a person, hand-overs and stale input, physical support); parameters `goal_release_s` and `contention_window_s`. The timeline rules themselves come with the metrics (M4).
45
+ - Event schema v0.3: `support_start` and `support_end` events (optional `scope`, `device_id`); `session_start.unobserved` (signal classes the recorder cannot see); heartbeat `clock_source` (`ntp`, `ptp`, `gnss`, `none`, `unknown`) and `clock_max_error_s`.
46
+ - `spec/v0.3/device_registry.schema.json` and `autonometry registry check FILE [--json] [--spec VERSION]`: validates a device registry (I-JSON, schema, no repeated `device_id`, no `shadowed_device` whose `match` holds every field of an earlier device's `match` with the same values, since the first match wins; at most 1 MiB) and prints the SHA-256 of its RFC 8785 form, the value of `session_start.device_registry_hash`. Exit status 0 valid, 1 invalid or unreadable, 2 bad usage. Values from the file are never printed. `autonometry.spec.load_registry_schema`.
47
+ - Loader flags `clock_unsynced` (a heartbeat reports `clock_source` `none` or `unknown`, reports none, or reports `clock_max_error_s` above `clock_tolerance_s` or not at all; or no heartbeat reports the clock) and `attribution_inferred` (events with `x_attribution_inferred: true`), each once per session with a count.
48
+ - `rules.toml`: `[params]` `goal_release_s`, `contention_window_s`; `[interventions]` `person_started_states`, checked to name known states.
49
+ - `autonometry record --config FILE` (Linux and macOS): the recorder. One process and one writer: a `selectors` loop owns a Unix datagram socket (0600, or 0660 with `socket_group`) and every file write; one background thread fetches RFC 3161 tokens. It writes `<data_dir>/<robot_id>/<session_id>/events.jsonl` and `events.checkpoints.jsonl`, sealed as it goes and checkpointed by the definitions.md 10.3 policy. It alone writes `session_start` (config scopes, weights, hint policy, `simulated`, `unobserved`, current states as `initial_states`, and the SHA-256 of the device registry, task registry and plan), `session_end`, heartbeats every `heartbeat_interval_s` (clock status from `adjtimex(2)` on Linux, `unknown` elsewhere; distance; loss counts), `seq` and `ts` (receive time, clamped so it never goes backwards; `x_clock_stepped_back_s`). Level-triggered states with scope ownership and `unknown` after `gap_tolerance_s` of silence (6.14); a gap in a connector's counter, a rejected datagram or the per-connector rate limit makes every scope `unknown` and is counted on the next heartbeat. Durability: one `write` per event on `O_APPEND`, fsync at most every `fsync_interval_s` and always before a checkpoint covers the events, the checkpoint file fsynced after every record. Crash recovery cuts a final partial line, verifies the chain, and continues the session (`x_recovered`, `x_truncated_bytes`, every scope `unknown`, a checkpoint); a closed session missing its final checkpoint gets it; a session that does not verify is left as it is; a changed config closes the session (`x_cause: "config_changed"`). SIGTERM/SIGINT end the session cleanly and wait up to 10 s for the last token; `max_log_bytes` rotates sessions (`x_cause: "rotation"`). Config: TOML, frozen pydantic model, unknown keys rejected; bad config, key or registry exit 2 with one line. A lock file keeps a second recorder away from the same robot folder.
50
+ - Recorder, after review: `device_registry` is required (without one, definitions.md 6.10 makes every source a remote human). Each session folder holds private copies of the device registry, task registry and plan it used (`devices.json`, `tasks.json`, `plan.json`, written atomically, 0600); checking them against the `session_start` hashes comes with M4. Session ids start with a per-robot counter (`000042-20261112T083000Z-7f3a`); the newest session is the one with the largest counter, never decided by time, because a computer without a real-time clock can boot with an old time. Crash recovery also checks the checkpoint file against the replayed events (size, root, origin, the recorder's own signature) and leaves a session that contradicts its own seals as it is. A connector first seen with `n` above 0 counts as `x_resumed_clients`, not as lost events. After a loss, rejection or rate limit, assertions restore scopes only after a quiet heartbeat interval, so a flood writes at most one `unknown` per scope per interval. A rotation repeats each known scope's full value right after the new `session_start`, which names a carried-over open task in `x_carried_task`.
51
+ - `clients/python/autonometry_client.py`: the connector client, one file, standard library only, Python 3.8 or newer. `Recorder(socket_path, client_name)` with `state(...)`, `event(type, ...)` and `distance(metres)`; every call returns at once and never raises; dropped events are counted (`dropped`) and still numbered, so the recorder sees the gap. The wire format is documented in the README.
52
+ - `fileio.make_private_dirs` (folders 0700), `keys.load_key_name`, `keys.vkey_path`, `reader.check_event` (the line checks of `validate` for a parsed value), `sealing.Sealer.checkpoint_now`, `timestamps.ns_to_ts`.
53
+ - CI: the client's tests on Python 3.8, and `mypy --strict` on the client.
54
+ - Slow measurements of the recorder: events per second in-process and over the socket, and memory over an hour of traffic at 50 events/s with a fake clock.
55
+ - Spec v0.3: `task_registry.schema.json` (`{"tasks": [{"task_definition_id", "description"?}]}`, at least one task) and `plan.schema.json` (`{"items": [{"plan_item_id", "kind": "reset" | "calibration" | "demonstration" | "approval_gate", "description"?}]}`, may be empty), hashed like the device registry; a repeated id is an error. definitions.md: the registry mapping of states (6.10, flag `registry_mismatch`), an initial control state counts as `human_remote` (`undeclared_device`), a `planned` reason without its plan item becomes `human_initiated` (`unplanned`, 3), every scope stays `unknown` after a gap in `seq` until its next transition (2), a dwell sits on [ts − dwell, ts) and only its overlap with `ai` time moves to human time (6.1), any intervention makes a task not autonomous (5), and the **Registries** paragraph of section 8 (flags `registry_missing`, `unknown_task_definition`). `rules.toml` `[flags]` gains the four codes.
56
+ - `autonometry.timeline`: `build_timeline(session, registries, rules)` turns one checked session into a frozen `Timeline`, pure and standard library only, with every time in integer nanoseconds (scope-weighted totals as exact fractions): per-scope spans with the initial-state, gap, seq-gap, registry, help-request, plan and idle-in-task rules applied; the whole-robot timeline by the any-human rule; time per state, observed, active, idle and unknown time per scope, whole robot and scope-weighted; intervention episodes (`takeover`, `shared`, `safety_stop`, `preset`) with their human time and whole-robot merging within `simultaneity_window_s`, approvals and hints with their dwell placed; safety stops (human-triggered or automatic, joined by a takeover or not); tasks (outcome, autonomous, unknown time inside, human time, unplanned resets since the previous task); planned and unplanned reset time; supervision ratio and supervisor-hours per robot-hour; human actor-time and distinct human actors; supported time; distance; and every flag in `(seq, code, detail)` order. `dwell_shift` recomputes the dwell moved from AI with other minimum dwells, for the M4b sensitivity variant. A 100k-event, six-scope session takes about 0.6 s.
57
+ - `autonometry timeline LOG.jsonl... [--devices F] [--tasks F] [--plan F] [--json]`: per session, time totals, interventions by kind and reason, safety stops, tasks, resets, supervision and flags. Registries come from the session folder (`devices.json`, `tasks.json`, `plan.json`) or the named files and must match their hashes in `session_start`. Exit 0; 1 when a registry does not match or is invalid (that session gets no timeline) or lines were rejected; 2 on bad usage.
58
+ - `autonometry registry check --kind devices|tasks|plan` (default `devices`); `registry.check_registry(path, version, kind)`, `registry.hash_problem`, `registry.LAYOUTS`, `spec.RegistryKind`; `Session.file`, the file holding the session's `session_start`.
59
+ - Golden timelines under `tests/golden/timeline/`, each expected file written by hand from the spec: two humans at once, a six-scope humanoid with simultaneous takeovers, `hint_as_shared` and `hint_as_event`, automatic stops with and without a takeover, stops a person triggered, a preset a person started, planned and unplanned resets, unknown gaps inside a task, an undeclared device and a registry mismatch. Hypothesis properties: state times sum exactly to observed time, spans tile the session, episodes never overlap within a scope, shuffled lines give the same timeline.
60
+ - Spec v0.3 and the timeline, after review: the silence limit is `heartbeat_interval_s + gap_tolerance_s` (section 2 text); a run of `unknown` and `safety_stop` between AI control and human control does not hide an intervention (3); an approval or hint inside an intervention episode belongs to it and is not counted again (3, `in_episode` in the JSON); unplanned human time while no task is open makes the next task not autonomous (5, 6.3); an approval wait longer than `approval_wait_max_s` logged as `ai` is repaired to `shared` with flag `approval_wait_recoded` (6.1; a transition, seq gap, task start, task end or other approval on the scope ends the wait, and the flag is raised only when some `ai` time changed); a transition the registry makes `human_local` or `human_remote` without a reason takes `human_initiated` (6.10); the help-request and plan-item checks apply to approvals and hints (3); a takeover joining an automatic stop may go into `shared`, and the window ends with the task (6.7).
61
+ - `autonometry.stats` (extra `report`; the core never imports NumPy, SciPy, statsmodels or pandas, and a test proves it): `intervals` (Wilson and Clopper-Pearson, cluster-robust GLM proportion, Garwood, NB2 rate with the multi-start fit and labelled fallbacks, ratio-linearised t, Wilson effective-n, percentile cluster bootstrap and bootstrap-t with relabelled clusters, dispersion index, Kaplan-Meier with statsmodels `SurvfuncRight`, exponential mean gap, Weibull shape with SciPy `CensoredData`), `metrics` (metrics (a) to (h) of the reference package, ported with its defects 2, 3 and 7 fixed: zero interventions, NumPy warnings, lifelines), `report` and `planning` (ported unchanged). Every statsmodels and SciPy fit runs with warnings as errors; an attempt that warns is rejected and the fallback's method name says why.
62
+ - `autonometry metrics LOG... [--alpha 0.05] [--n-boot 2000] [--seed 0] [--cluster auto|session|robot] [--drift-bin day|week] [--out FILE]`: the metrics of the standard as JSON, one block per group (real, simulated), byte-identical for the same logs and options. A session without a timeline is left out and named on stderr (exit 1); without the `report` extra, one line and exit 2.
63
+ - Spec v0.3 rules for statistics (definitions.md "Changes in v0.3", items 21 to 29): bounds `unknown_as_human` and `unknown_as_ai` for the time autonomy share, task autonomy rate, autonomous success rate, interventions per active hour and human minutes per task, with their formulas in section 7; the outer interval spans every limit of the point estimate and both bounds; one report block per group; headline rates use whole-robot counts.
64
+ - `Timeline.support` (the union of support intervals), `Timeline.distance_samples` and `Timeline.human_actors`; `supported_ns` and `distinct_human_actors` are now properties derived from them.
65
+ - Golden metrics set under `tests/golden/metrics/` (two robots, real and simulated sessions) with its point estimates and bounds checked by hand; hypothesis properties for the report (no crash, no NaN, bounds bracket the estimate); a slow test of the coverage of the default intervals on the simulation study's baseline scenario.
66
+ - Spec v0.3 section 11, "Report" (normative; change 33): provenance, trust, session census, exclusions, metrics per group, the statements printed beside them, and invariants.
67
+ - `autonometry report LOG... --out DIR [--vkey FILE]... [--tsa-root PEM]... [--anchor FILE]... [--devices/--tasks/--plan F] [--alpha --n-boot --seed --cluster --drift-bin] [--title TEXT] [--about FILE] [--spec V]`: writes `report.json` and a self-contained `report.html` (inline CSS and SVG, no scripts, web fonts or external links; prints to PDF) atomically. Verdict box (trust status and level, completeness, headline numbers with CI and outer interval), trust per session, census of recorder-form session counters, exclusions, the metrics JSON per group, statements, invariants, a sessions table, parameters and provenance (SHA-256 of every input and spec file). Failed sessions stay in the metrics and the top says `FAILED`. No actor ids; deterministic apart from `generated_at`. Exit 0 only when verified at L2, complete, no census gap and nothing left out; 1 otherwise; 2 on usage or input errors.
68
+ - `autonometry.report` (extra `report`): `build` (pure functions from logs, verification and timelines to the report dict), `trust`, `census`, `statements`, `invariants`, `figures` (four matplotlib SVG figures in Okabe-Ito colours, fixed hash salt, no date metadata, ids prefixed per figure) and `html` (jinja2 with autoescape; only the package's own SVG is marked safe).
69
+ - `stats.intervals.garwood_upper` (one-sided exact Poisson upper bound) and `kaplan_meier_curve` (the steps and pointwise band of a Kaplan-Meier curve).
70
+ - Report, after review: a failed invariant makes the report not clean (exit 1) and is listed in the verdict; the statement thresholds are `rules.toml` `[report]` `rare_rate_events = 5` and `min_clusters = 2`, named in definitions.md 11.6 and printed with the parameters; the verdict box is green only when clean, amber when not clean, red when failed; the state table says how much AI time approval and hint dwell moved; the gap median shows an open limit as "not reached" and durations in s, min or h, as does the Kaplan-Meier axis; times are shown to the second; each metric's method and notes share a detail row under it (no Notes column), so the metrics table fits the page at 1100 px and on an A4 print, and on a narrow screen a wide table scrolls inside its own box; statements are grouped by group and kind. `markupsafe` is declared in the `report` extra.
71
+ - `autonometry metrics`: human-minutes-per-task intervals (point and bounds, and per task definition) are clipped at 0, and the method name ends in `, clipped at 0` when a limit moved.
72
+ - Extra `report`: matplotlib floor raised from 3.8.0 to 3.10.7, the first release that does not call pyparsing names deprecated in pyparsing 3.3 (with warnings as errors, older releases fail on import).
73
+ - `recorder.store.SESSION_ID_RE` and `COUNTER_DIGITS` are public, so the census reads the recorder's session id form.
74
+ - Golden reports under `tests/golden/report/`: the metrics logs unsealed (L1) and sealed with the fixed-seed test key, checkpoints and an anchor file (L2); `tests/report_helpers.py` regenerates them.
75
+
76
+ - TurtleBot 4 connector (M6). `autonometry.connectors.tb4`: a pure, standard-library engine that turns typed observations of a TurtleBot 4's ROS 2 topics (Nav2 goal status, `/cmd_vel` with its publisher, `/cmd_vel_smoothed`, collision monitor, `/function_calls`, buttons, `/joy`, wheel, dock and kidnap status, `/initialpose`, `/odom`) into state assertions for the scope `base`, task events, hints and the odometer, by the integration notes' table B.2 under spec v0.3 (README, "TurtleBot 4"). `connectors/ros2/autonometry_ros`: an ament_python package with a read-only rclpy node (no publisher, no `/rosout`, no parameter services, no service or action calls; transient-local QoS for action status), a launch file and a params file; publisher identity from the ROS graph, recorded as `x_publishers`, with the topic-level rule and `x_attribution_inferred` where the message does not show its sender (ROS 2 Jazzy). Example device registry, task registry, plan and recorder config, and an untested simulator demo for Windows 11 with WSL2 (`demo/tb4-sim/`). Golden test `tests/golden/tb4/`: the notes' 17-event example rebuilt as observations, run through the engine, the recorder and the timeline, with the v0.3 differences explained. CI job running the observer, the recorder and fake TurtleBot 4 nodes in a `ros:jazzy` container pinned by digest.
77
+ - TurtleBot 4 connector, after review: Nav2's relay chain is checked. A command the collision monitor relays is `ai` only when every publisher of `cmd_vel_nav` and `cmd_vel_smoothed` is a declared AI and a Nav2 task or an AI-started routine runs (or ended less than `gap_tolerance_s` ago); otherwise a person's, `shared` while an AI action runs, else `human_remote`. The observer follows the chain topics' publishers (`ChainPublishers`) and every Nav2 action that moves the base: `navigate_to_pose`, `navigate_through_poses` and `follow_waypoints` open tasks; `spin`, `backup`, `drive_on_heading` and `assisted_teleop` are routines like dock and undock, `shared` when a person or an undeclared client starts them. A node name two publishers share on a command topic matches no device. A person's routine, or a person's hint, from an undeclared device is now sent as `shared` (the timeline counts it `human_remote`). The example registry declares Nav2's whole chain; the task registry adds `nav_through_poses`. The CI scenario adds a hidden node driving through `/cmd_vel_smoothed` and a `drive_on_heading` goal from an undeclared client, both checked as human time.
78
+ - The recorder accepts a client `heartbeat` that carries only `x_` fields it does not write itself, and writes them on a heartbeat of its own with the clock status and distance (how a connector records `x_publishers`).
79
+
80
+ - Spec v0.3 section 12, "Witnesses (trust level L3)" (change 35): a teleoperation station keeps its own sealed log (`session_start.role` `station`, `station_id`); pairing, the tolerance tau (both clocks' reported errors plus the new parameter `react_s`, 0.5 s), per-scope classification, episodes and the L3 verdict; station input while the robot logs `unknown` is reported on its own line and counts as station-only time in the L3 conditions. Schema fields `role` and `station_id` (only on `session_start`, `station_id` exactly when the role is `station`); `rules.toml` `[params] react_s` and `[trust]` `l3_reasons`, `l3_robot_human_states`, `l3_station_states`.
81
+ - `autonometry.witness`: `reconcile`, pure and exact (integer nanoseconds): per robot session the cells both-human (with its tolerated and held parts), robot-only, station-only, neither and unwitnessed, the shares, tau, matched and unmatched episodes, an upper-bound intervention count, and the L3 verdict with its reasons.
82
+ - `autonometry reconcile --robot LOG... --station LOG... [--vkey] [--tsa-root] [--anchor] [--out]`: verifies both sides in one pass and writes the reconciliation as JSON; exit 0 only when every robot session is L3.
83
+ - `autonometry report --station LOG...`: robot sessions whose witnesses agree are L3, the report's level can be L3, a Witness section shows the reconciliation and what L3 cannot show, and the witnesses invariant is `pass`, `fail` or `not_available`.
84
+ - Recorder config `role` and `station_id`: a teleoperation station's recorder writes them on `session_start`.
85
+ - `autonometry.connectors.station` and the ROS 2 node `station_observer` (`station.launch.py`, `config/station.params.yaml`): the station's witness on the operator's PC, asserting `human_remote` while its keyboard or joystick input is live, `unknown` when the joystick is lost, `idle` otherwise. `clients/python/station_example.py` does the same for any teleop stack. Demo configs `demo/tb4-sim/config/station-recorder.toml` and `station-devices.json`.
86
+ - CI: the ROS job also runs an honest TurtleBot 4 run with a station recorder and observer (`connectors/ros2/ci/run_witness.sh`) and requires L3.
87
+ - Timelines carry the session's `role`, `station_id` and the clock status of every heartbeat; the timeline JSON shows `role` and `station_id`.
88
+ - LeRobot (SO-101) connector, for LeRobot v0.6.1 at commit `7e241bd6` (README, "LeRobot"). `autonometry.connectors.lerobot` is pure and uses the standard library only. A parser for the log lines `lerobot-rollout` writes (DAgger `Phase transition`, episode start and save, re-record, loop end, teardown, `Rollout finished`; each cites its source line). A live engine: DAgger `AUTONOMOUS` is `ai` with actor `ai:<policy path>`; `PAUSED` is `shared` with `human_initiated` (6.8); `CORRECTING` is `human_local`, or `human_remote` through a teleoperator declared remote (6.10). Episodic resets are `planned` human time with a teleoperator, else `scripted`. LeRobot's return to the start pose is `scripted` (6.9, 6.13). A stopped controller holds until idle (6.14). Episodes are tasks, outcome `unknown`, or `aborted` on re-record. The log's local time travels as `x_src_ts` with `x_src_clock` `wall`. Sources are matched in the device registry as `match.node` `policy:<path>`, `teleop:<type>` and `script:return_to_initial_position`. An importer turns one LeRobotDataset v3.0 episode into one session: transitions at each change of `intervention`, `unknown` for missing frames or labels, heartbeats every second, `ts` = anchor + `frame_index / fps`, `x_ts_reconstructed: true` on every event (trust level L1), `unobserved: ["safety_stops", "human_inputs"]`.
89
+ - `autonometry lerobot watch --log FILE --config RECORDER.toml [--task] [--policy] [--pid] [--from-start]`: follows a rollout's log like `tail -F` and sends the robot's state and tasks to the recorder through the recorder client (Linux and macOS). It watches the rollout's process, read-only, about once a second (psutil; `--pid`, or the `lerobot-rollout` process found on this computer, the deepest of a launcher and its Python). A rollout that ends without logging `Rollout finished` (killed, out of memory, a native crash, its window closed) has its open task closed as `unknown` and every scope made `unknown`, after the lines left in its log, with `x_lerobot_process_exit: unlogged` on a heartbeat. With no single process to watch it notes `x_lerobot_liveness: unwatched`. `autonometry lerobot import DATASET_DIR --out DIR --registry DIR [--robot-id] [--start] [--policy] [--teleop] [--simulated]`: writes one log per episode with the registries beside it. Both import the `lerobot` extra (pyarrow, watchfiles, psutil) only when they run. psutil is new in the extra: it lists and watches processes on Linux, macOS and Windows without ever signalling them, and its `is_running` survives pid reuse.
90
+ - Rollout logs written on Windows: the follower honours a byte-order mark (UTF-8, UTF-16 LE or BE; Windows PowerShell 5.1 writes `Tee-Object` and `>` files as UTF-16), reads bytes that are not UTF-8 as U+FFFD, the parser accepts LeRobot's em dash in any code page and skips the `<program> : ` PowerShell 5.1 puts before the first line of stderr. README commands for PowerShell and cmd, and `PYTHONUNBUFFERED=1` so the re-record key's printed line arrives in order. Captures from Windows PowerShell 5.1 and cmd are test fixtures.
91
+ - `connectors/lerobot/`: example device registry, task registry, plan and recorder config for an SO-101. `demo/lerobot/run_demo.py`: downloads a public DAgger dataset's metadata and first data files from the Hugging Face Hub with urllib (pinned revision, size printed first, stops above 100 MB), imports it and writes a report: time autonomy share 0.779, 95 % CI [0.668, 0.891], 20 interventions, as dossier 14.6.
92
+ - Tests: LeRobot rules named by spec section and dossier row; Hypothesis over random log lines and random intervention columns; goldens (a rollout log through the engine and the recorder, a tiny dataset built with pyarrow); an acceptance test against the Hub, marked `network` and skipped by default (`uv run pytest -m network`).
93
+
94
+ - Unitree G1 connector (README, "Unitree G1"; design in `research/unitree-g1/`). `autonometry.connectors.g1`: a pure engine for the scopes `legs`, `upper_body` and optional `hands`, implementing the integration notes' rules (section 6) under spec v0.3, first rule that applies: robot lost, emergency stop (the remote's L2 + B, or Damp or ZeroTorque from a person, followed by a passive state), released or user control (the writers of `rt/lowcmd` or `rt/user_lowcmd`), built-in controller unseen, other passive states (the hold of whoever caused it), the upper body (arm presets, `rt/arm_sdk` writers and their blend weight) and the legs (goal spans from the remote's sticks and buttons and from sport requests, postures, unexplained motion, the built-in controller balancing in a task), and the hands. Thresholds from `rules.toml` plus the notes' constants (stick dead zone 0.01, weight limits 0.01 and 0.99, preset hold 20 s). Evidence in `x_g1_cause`, `x_g1_fsm_id`, `x_g1_weight`, and on heartbeat notes `x_g1_writers` and `x_g1_tasks`. Where the spec and the notes leave a case open the engine takes the more conservative state, never `ai`, marked `Open case` in the code. `registry_problems` refuses a device registry in which one observed source `(host, participant, topic)` could match devices of different kind or location; virtual topics `wireless_remote` (the handheld remote inside `rt/lowstate`) and `preset` (Unitree's own fixed motions).
95
+ - `autonometry.connectors.g1_dds`: the subscribe-only edge on Cyclone DDS 0.10 (no DataWriter, no RPC request; best-effort volatile readers, `rt/lowstate` keep-last 1, commands and requests keep-last 64). Cyclone DDS is not a declared dependency and is imported only inside functions, after a version guard that refuses anything but the 0.10 line in one line (an observer on 11.1 crashed 0.10 publishers). Message types written from unitree_sdk2_python 814556d1 and unitree_sdk2 aaf69cb5 IDL field lists (BSD-3-Clause); a reader opens only for a writer of the expected type, any other counts by its presence. Writer identity from DCPSParticipant properties and DCPSPublication, with Python scripts on the watcher's computer named through psutil while their pid still runs that program. Tasks come from the recorder's current session log, followed read-only.
96
+ - `autonometry g1 discover [--interface] [--domain 0] [--seconds 5] [--json]` (participants with host, program and pid, what they publish, and the exact match strings) and `autonometry g1 watch --config FILE [--interface] [--domain 0]` (Linux and macOS). New extra `g1` (psutil). Example registries, plan and recorder config in `connectors/g1/`.
97
+ - Unitree G1, after review: the G1 commands refuse any Python but 3.12 in one line (Cyclone DDS 0.10 for Python does not load on 3.13), and a binding that fails to load ends in one line too, not an unexpected failure. `x_g1_writers` names a declared writer by topic and `device_id` only and an undeclared one by topic, program and a pseudonym of its host (the first 12 hex digits of the SHA-256 of the host name, never the name); `discover` prints the pseudonym beside each host. When `rt/lowstate` returns after more than `idle_threshold_s` while released or in user control, the watcher keeps the mode, logs one warning and notes `x_g1_robot_returned` on a heartbeat.
98
+ - Tests: one per rule citing the spec section; Hypothesis properties (random observations never crash, `legs` is never `ai` while a person's goal span is open, every scope is `unknown` once the robot is lost, determinism); version guard; registry ambiguity; a golden session through the recorder to a report, hand-worked first (`tests/golden/g1/`). CI job `dds` (Linux and macOS): Cyclone DDS C 0.10.5 built from its tag (cached) and the binding from source, five fake G1 programs in separate processes over loopback, a second participant confirming through DCPSPublication that the observer has no writer, and `discover` and `watch` end to end with a recorder (tests marked `dds`, skipped elsewhere).
99
+
100
+ - `autonometry demo --out DIR` (needs the `report` extra): the whole chain on a synthetic session shipped in the package (`autonometry/demo/session/`: one twelve-minute warehouse shift with six tasks, a remote takeover, an automatic safety stop and 15 s unknown), with no robot, no network and no source checkout. It generates a throwaway Ed25519 key in `DIR/key/` (0700), seals, verifies and explains in plain words why the result is L1 and not L2 (no timestamp authority), and writes `verification.json`, `report.json` and `report.html`, naming each file and the one to open. Paths in its outputs are relative to `DIR`; two runs differ only in the key, its signatures and `generated_at`. It refuses a folder that is not empty and prints one line without the `report` extra. `tests/demo_helpers.py` rebuilds the shipped log from its scenario.
101
+ - Wheel smoke test, `scripts/smoke_wheel.sh`: `twine check --strict` on the sdist and wheel (the README must render on PyPI), the wheel installed with the `report` extra in a fresh virtual environment outside the source tree, then `autonometry --version` (must match the wheel), `autonometry spec show` and `autonometry demo`. CI job `wheel` on Linux, macOS and Windows; in `release.yml` the job `smoke` runs it on the files to be published, before `publish`.
102
+ - Release automation: after `publish`, the job `github-release` (permission `contents: write`, that job only) creates the GitHub release for the tag with the CHANGELOG section as notes and attaches the sdist, the wheel, `spec-v<spec>.zip` (byte-identical for the same folder) and `SHA256SUMS`, assembled by `scripts/release_assets.py`. A test checks that CHANGELOG.md has a section for the package version.
103
+ - `sealfile.seal_sessions`, the sealing that `autonometry seal` and the demo share.
104
+ - `CONTRIBUTING.md` (setup, the definition of done, commit style, proposing a spec change through `docs/spec-v0.4-proposals.md`), `CODE_OF_CONDUCT.md` (Contributor Covenant 2.1), `CITATION.cff`, issue templates (bug, feature or connector, spec change) and a pull request template; a status note in `spec/README.md` (draft, versioning, feedback through issues).
105
+
106
+ ### Changed
107
+
108
+ - The spec draft v0.2 became v0.3 in place (`spec/v0.3/`, schema `$id`s, `spec_version = "0.3"`); nothing had been released, so no v0.2 is kept.
109
+ - `session_start` must carry `simulated` (boolean); a `session_start` without it is rejected with `schema_violation`. Reports must not pool simulated and real sessions.
110
+ - `verify` caps a session holding events with `x_attribution_inferred: true` at trust level L1 with gap code `reconstructed`, as for `x_ts_reconstructed`.
111
+ - The golden sealed log moved to 2026-11-12 timestamps and its RFC 3161 fixture was recorded again, because its notes changed with `simulated`.
112
+ - Minimum dependency versions lowered to the oldest releases that provide the features we use and carry no known advisory for them (`uv.lock` keeps exact pins). The tooling run by `uv run pytest` now also excludes `research/` from mypy and from `pytest .`.
113
+ - Slow statistical tests run with `uv run pytest -m slow --no-cov`.
114
+ - Logging is configured once by the console entry point: warnings and errors print as one clean line without exception text; an unexpected crash prints only `error: unexpected failure (<type>)` unless `--verbose` is given.
115
+ - `Rules.states` is a read-only mapping, so loaded rules can be shared and cached.
116
+ - Project URLs, `SECURITY.md` and `RELEASING.md` use the exact GitHub owner name `Autonometry` (PyPI Trusted Publishing compares it); release tags are annotated (`git tag -a`).
117
+ - The recorder checks its task registry and plan against their schemas at start and exits 2 with one line when one is not valid (it used to accept any JSON file); `recorder.config.json_hash` is replaced by `registry_file_hash`.
118
+ - `registry check --json` reports `kind` and `entries` instead of `devices`.
119
+ - A reader refuses a spec whose `[flags]` codes differ from the ones the reader and the timeline raise.
120
+ - Whole-robot state (spec v0.3, section 6.6): `unknown` now comes right after the human states, so an unknown scope beside an AI-controlled one is unknown time, never AI time (`rules.toml` `whole_robot.precedence`). No timeline golden changed.
121
+ - The timeline takes `distance_m` from every event that carries it, not only heartbeats.
122
+ - Spec v0.3: overlapping dwell windows count once (6.1); the supported share is supported active time over active time (6.15); distance at an instant is the latest `distance_m` at or before it, 0 before the first sample (8).
123
+ - Section 11 of the spec and the report: L3 is shown when station logs are given (11.2), a station log given as a robot log is left out of the metrics and the census (11.4), and the witnesses invariant is defined (11.7). `metrics` lists station sessions apart and never assigns them to a group.
124
+ - Spec v0.3 section 6.14 (change 34): outside an open task, a scope that no source commands keeps its last controller's state until it is idle; `unknown` is for lost input, not for a robot seen standing still. The golden reports were regenerated for the new spec file hash.
125
+
126
+ - The README is written for newcomers: what Autonometry is, the 60-second demo, install, how it works, the connectors and their status, the trust levels in plain words, and links that are absolute GitHub URLs so they work on PyPI. The reference moved without loss to `docs/`: `cli.md`, `recorder.md`, `verification.md`, `metrics-and-report.md`, `witness.md` and `connectors/turtlebot4.md`, `lerobot.md`, `unitree-g1.md`; code, configs and tests cite those pages.
127
+ - Development status Alpha (classifier `Development Status :: 3 - Alpha`), version 0.1.0.
128
+
129
+ ### Fixed
130
+
131
+ - Overlapping approval and hint dwell windows moved the same AI time more than once, which could push the time share below 0 and a task's human time above its duration. The AI time inside the union of the windows now moves once, for every scope, the whole robot, the scope-weighted and unsupported totals, and each task (within the task's own time); each item's own `moved_from_ai_s` is unchanged.
132
+ - The Kaplan-Meier median is the smallest gap with S(t) ≤ 0.5, as lifelines and the reference package (it was the first with S(t) < 0.5), and its interval is the medians of the pointwise cloglog band, the method the simulation study validated.
133
+
134
+ - Unitree G1: `rt/lowstate` (the robot's presence and the handheld remote's bytes) and `rt/arm/action/state` are believed only from the robot, the host of the declared built-in controller's `rt/sportmodestate` writer (remembered once seen, so the robot is not lost when that writer goes away in debug mode). Samples from any other computer, or from a writer discovery has not shown, are ignored and noted once per writer (`x_g1_ignored_robot_state`): a forged lowstate can no longer keep a silent robot present or pass for the remote, and a forged arm state cannot end a preset (security review S-22).
135
+ - TurtleBot 4: a publisher map larger than one heartbeat is sent in numbered parts of at most 48 KiB (`x_publishers_part: [i, n]`; `autonometry.connectors.heartbeats`), never dropped until the publishers change; an entry too large for any part would be counted in `x_publishers_dropped` (security review S-55).
136
+ - LeRobot: with `--from-start`, a rollout read from the part of the log written before the watcher started, whose process cannot be found once that part is read, is taken as gone: the open task closes with outcome `unknown`, every scope is `unknown` and a heartbeat notes `x_lerobot_process_exit: not_found`, instead of keeping the state the log last showed. A later line from that rollout re-attaches it, keeping the policy and teleoperator already learned; the state is `unknown` until a line tells it.
137
+
138
+ - Reports and JSON outputs are the same on every system (product rule 4): every file name in `report.json`, `report.html`, `verify --json`, `registry check --json`, rejected lines, file problems and the demo's files is written with forward slashes and stays relative where it was given relative (`autonometry.paths.path_text`); a path given with backslashes on Windows gives byte-identical output to the same path with forward slashes.
139
+
140
+ ### Security
141
+
142
+ - `validate` output cannot carry terminal control codes from a log: robot and session ids, the only event values it prints, must match `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`, so a line with escape codes in them is rejected (`schema_violation`) and never echoed.
143
+ - Pre-release security review M10 ([`docs/security-review-2026-11.md`](docs/security-review-2026-11.md)) and threat model ([`docs/threat-model.md`](docs/threat-model.md)), linked from `SECURITY.md`. Every attack is kept as a test (`tests/test_security_*.py`, `tests/client/test_client.py`). Fixed:
144
+ - The verifier crashed (`ValueError`) on a checkpoint whose tree size had more than 4300 digits; a size must now be an ASCII decimal below 2^64, else the note is `malformed_checkpoint` (S-01).
145
+ - A schema-valid `heartbeat_interval_s`, `dwell_s` or `clock_max_error_s` of 1e308 crashed `validate`, `timeline`, `metrics`, `report` and `reconcile` (`OverflowError`); durations now saturate at 2^70 ns, longer than any two timestamps can be apart, so every comparison is unchanged (S-02).
146
+ - Arrays and objects may nest at most 128 deep in a log line, checkpoint record, registry or recorder datagram, else the line is `invalid_json`. A line nested about 990 deep used to parse or not depending on the caller's stack, then crash hashing with `RecursionError` in `verify` and inside the recorder (S-03).
147
+ - The recorder died on a datagram whose counter `n` jumped past 2^53 twice (an `x_lost_client_events` too large for I-JSON) or whose odometer readings summed to infinity; counts now saturate at 2^53 − 1, such a reading is rejected, and any datagram the engine fails on is counted as rejected and makes every scope `unknown` instead of stopping the recorder (S-04).
148
+ - A config of deeply nested TOML arrays crashed `autonometry record` instead of a one-line error (S-05).
149
+ - The recorder creates its socket with its final mode and sets its group without following a link, refuses a socket folder or a `data_dir` every user can write (without the sticky bit) and a robot folder another user owns or can write, opens its logs with `O_NOFOLLOW`, and turns a stale socket it cannot remove into a start error (S-06, S-07).
150
+ - A signing key's permissions are checked on the file opened, not by path after reading it, and a key file over 64 KiB is refused (S-08).
151
+ - Text the command line prints (file names, messages naming input) shows control, format and separator characters as `\xNN` / `\uNNNN` escapes (S-09, S-52).
152
+ - Matching robot runs against station episodes was quadratic: 8000 of each took 40 s in `reconcile` and `report --station` (S-10).
153
+ - The recorder client deadlocked when a signal handler sent an event while its thread was inside a send; its lock is reentrant, and a path or name whose `str()` raises no longer raises (S-11).
154
+ - Unitree G1: a participant `__Pid` of non-ASCII digits (such as `²`) crashed `g1 discover` and every `g1 watch` pass that saw it, and the task change of that pass was lost; a refused mode request (anyone can send one) ended an emergency stop; an unreadable `rt/arm_sdk` weight kept the writer's previous weight instead of counting as 1; a huge request number made an actor id the recorder rejects; goal spans are capped at 1024; the arm state is parsed as sent; `discover` groups writers once (S-20, S-21, S-23 to S-25, S-28, S-30).
155
+ - TurtleBot 4 and station: a huge `/cmd_vel_smoothed` made the relay check raise, which dropped every `/cmd_vel` of an unknown publisher, a person's included (S-41).
156
+ - LeRobot: a log that vanishes, is replaced or is cut makes the state `unknown` (`x_lerobot_log: lost`) instead of keeping it; a line flushed after the rollout's process is known gone no longer brings its state back; repeated episode starts no longer cost quadratic time; the importer bounds episodes (24 h), rows (10 million per file) and indexes (2^53 − 1) before writing, refuses to replace an existing session log or to write outside `--out` (`C:` or `NUL` as robot id on Windows), and turns unreadable columns and impossible times into one-line errors (S-40, S-43, S-44, S-49 to S-51, S-53).
157
+ - CI: a `workflows` job runs zizmor 1.32.0 (offline, auditor persona) on the workflows; the release workflow has a concurrency group and documents its `id-token` permission; the audit job quotes its ignore list.
158
+ - Security review M10, the owner's decisions on its open points (see the review's "Open points and their decisions"; spec changes they call for are proposals in [`docs/spec-v0.4-proposals.md`](docs/spec-v0.4-proposals.md)):
159
+ - Only L2 is "verified": `verify --require L1` printed "result: verified" and returned `"verified": true` for an unverified session. It now prints `result: meets L1` (and whether the log is verified at L2), and the JSON adds `meets_required_level`, with `verified` true only at L2 (S-64).
160
+ - A session whose `session_end` no anchored checkpoint covers gets a warning in `verify` (events after the last anchor, at its time, may have been removed) and makes the report not clean; session verdicts carry `last_anchored_ts`, `end_anchored` and `warning`. The README and threat model say to anchor often and keep anchor files off the robot (S-14).
161
+ - The recorder never fills the robot's disk: below `min_free_bytes` (default 1 GiB) free on the data folder's disk it stops writing with one error, drops datagrams unread and resumes when space returns with a heartbeat carrying `x_disk_full_s` and `unknown` on every scope (S-15).
162
+ - The 128-level JSON nesting limit is documented, and proposed for `rules.toml` with the other input limits (S-03).
163
+ - Unitree G1: an emergency stop holds until the robot confirms a change of mode (a success response from the built-in controller, or `rt/sportmodestate` leaving passive); RPC responses are believed only from the built-in controller's identity (others noted as `x_g1_ignored_response`); two writers claiming one identity at once are both undeclared (`x_g1_duplicate_identity`); an undeclared writer's program appears as a pseudonym like its host, and `discover` prints both (S-21, S-22, S-26, S-27).
164
+ - LeRobot: the policy appears as `ai:<device_id>` or `ai:<pseudonym of its path>`, never the path; logged signals need LeRobot's log prefix, and an unprefixed line counts only as the exact printed re-record message, checked against messages quoted from the pinned source (S-47, S-48).
165
+ - TurtleBot 4: one relayed command per `/cmd_vel_smoothed` message, so a person driving in Nav2's direction is `shared`; a task status or feedback counts only from declared Nav2 nodes; Nav2 leaving the graph or silent for more than `gap_tolerance_s` mid-task makes the scope `unknown`; node names reach `x_publishers` only as valid ROS 2 names, else as pseudonyms. The observer reads Nav2's feedback (`nav2_msgs` action types) and the action servers (S-42, S-46, S-55).
166
+ - Station witness: `/joy` counts only when every publisher of the topic is a declared station device; the `joy_node` parameter is removed (S-45).
167
+
168
+ ### Removed
169
+
170
+ - The `License :: OSI Approved` classifier: the SPDX `license` field replaces it (PEP 639).
171
+
172
+ [Unreleased]: https://github.com/Autonometry/autonometry/compare/v0.1.0...HEAD
173
+ [0.1.0]: https://github.com/Autonometry/autonometry/releases/tag/v0.1.0
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.