mostlyright-data 0.9.0__py3-none-any.whl

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 (314) hide show
  1. mostlyright/data_harness/__init__.py +158 -0
  2. mostlyright/data_harness/acquisition/__init__.py +55 -0
  3. mostlyright/data_harness/acquisition/http.py +2773 -0
  4. mostlyright/data_harness/acquisition/parsing.py +809 -0
  5. mostlyright/data_harness/acquisition/ranges.py +495 -0
  6. mostlyright/data_harness/acquisition/result_download.py +360 -0
  7. mostlyright/data_harness/acquisition/retention_admission.py +248 -0
  8. mostlyright/data_harness/acquisition/sandbox.py +4888 -0
  9. mostlyright/data_harness/acquisition/url_policy.py +530 -0
  10. mostlyright/data_harness/agent_runtime.py +2743 -0
  11. mostlyright/data_harness/assets/logo-ink.svg +31 -0
  12. mostlyright/data_harness/backends/__init__.py +28 -0
  13. mostlyright/data_harness/backends/pandas_backend.py +350 -0
  14. mostlyright/data_harness/backends/polars_backend.py +366 -0
  15. mostlyright/data_harness/backends/protocol.py +124 -0
  16. mostlyright/data_harness/backends/reference.py +83 -0
  17. mostlyright/data_harness/backends/registry.py +55 -0
  18. mostlyright/data_harness/backends/restrictions.py +126 -0
  19. mostlyright/data_harness/canonical.py +333 -0
  20. mostlyright/data_harness/catalog_job.py +625 -0
  21. mostlyright/data_harness/cli.py +5398 -0
  22. mostlyright/data_harness/contracts.py +53 -0
  23. mostlyright/data_harness/coordinator.py +1307 -0
  24. mostlyright/data_harness/deploy.py +924 -0
  25. mostlyright/data_harness/deploy_target.py +312 -0
  26. mostlyright/data_harness/deployment_evidence.py +1067 -0
  27. mostlyright/data_harness/event_presentation.py +576 -0
  28. mostlyright/data_harness/events.py +2152 -0
  29. mostlyright/data_harness/fast_delimited.py +239 -0
  30. mostlyright/data_harness/fleet.py +237 -0
  31. mostlyright/data_harness/formats.py +236 -0
  32. mostlyright/data_harness/governors.py +1163 -0
  33. mostlyright/data_harness/hosted_bootstrap.py +972 -0
  34. mostlyright/data_harness/hosted_crawler.py +1115 -0
  35. mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
  36. mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
  37. mostlyright/data_harness/hosted_crawler_job.py +1277 -0
  38. mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
  39. mostlyright/data_harness/hosted_dataset.py +1500 -0
  40. mostlyright/data_harness/hosted_deploy.py +3037 -0
  41. mostlyright/data_harness/hosted_handoff.py +62 -0
  42. mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
  43. mostlyright/data_harness/hosted_ingestion_job.py +356 -0
  44. mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
  45. mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
  46. mostlyright/data_harness/hosted_session_worker.py +3554 -0
  47. mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
  48. mostlyright/data_harness/hosted_worker.py +6784 -0
  49. mostlyright/data_harness/ingestion/__init__.py +56 -0
  50. mostlyright/data_harness/ingestion/contracts.py +461 -0
  51. mostlyright/data_harness/ingestion/faults.py +42 -0
  52. mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
  53. mostlyright/data_harness/ingestion/spool.py +130 -0
  54. mostlyright/data_harness/ingestion/store.py +885 -0
  55. mostlyright/data_harness/key_seam.py +434 -0
  56. mostlyright/data_harness/linux_process_boundary.py +262 -0
  57. mostlyright/data_harness/local_contracts.py +2880 -0
  58. mostlyright/data_harness/local_search/__init__.py +5 -0
  59. mostlyright/data_harness/local_search/build_index.py +1087 -0
  60. mostlyright/data_harness/local_search/contracts.py +920 -0
  61. mostlyright/data_harness/local_search/query_trace.py +266 -0
  62. mostlyright/data_harness/local_search/retrieval.py +700 -0
  63. mostlyright/data_harness/local_search/sealed.py +474 -0
  64. mostlyright/data_harness/local_search/service.py +784 -0
  65. mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
  66. mostlyright/data_harness/nbrender/__init__.py +12 -0
  67. mostlyright/data_harness/nbrender/chrome.py +359 -0
  68. mostlyright/data_harness/nbrender/code_body.py +266 -0
  69. mostlyright/data_harness/nbrender/document.py +407 -0
  70. mostlyright/data_harness/nbrender/frame.py +275 -0
  71. mostlyright/data_harness/nbrender/interactive.py +337 -0
  72. mostlyright/data_harness/nbrender/markdown_body.py +477 -0
  73. mostlyright/data_harness/nbrender/mr_components.py +134 -0
  74. mostlyright/data_harness/nbrender/outputs_data.py +595 -0
  75. mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
  76. mostlyright/data_harness/nbrender/outputs_source.py +260 -0
  77. mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
  78. mostlyright/data_harness/nbrender/outputs_text.py +400 -0
  79. mostlyright/data_harness/nbrender/parse.py +394 -0
  80. mostlyright/data_harness/nbrender/status.py +40 -0
  81. mostlyright/data_harness/nbrender/tokens.py +1295 -0
  82. mostlyright/data_harness/notebook.py +1710 -0
  83. mostlyright/data_harness/offline.py +2049 -0
  84. mostlyright/data_harness/operation_registry.py +1007 -0
  85. mostlyright/data_harness/operator_setup.py +239 -0
  86. mostlyright/data_harness/pipeline.py +6428 -0
  87. mostlyright/data_harness/plan_graph.py +2026 -0
  88. mostlyright/data_harness/preparation/__init__.py +104 -0
  89. mostlyright/data_harness/preparation/contracts.py +1017 -0
  90. mostlyright/data_harness/preparation/engine.py +221 -0
  91. mostlyright/data_harness/preparation/errors.py +14 -0
  92. mostlyright/data_harness/preparation/gates.py +751 -0
  93. mostlyright/data_harness/preparation/joins.py +574 -0
  94. mostlyright/data_harness/preparation/profile.py +384 -0
  95. mostlyright/data_harness/preparation/table.py +217 -0
  96. mostlyright/data_harness/preparation/transforms.py +568 -0
  97. mostlyright/data_harness/progress_events.py +534 -0
  98. mostlyright/data_harness/readers/__init__.py +46 -0
  99. mostlyright/data_harness/readers/containers.py +963 -0
  100. mostlyright/data_harness/readers/contracts.py +542 -0
  101. mostlyright/data_harness/readers/delimited.py +257 -0
  102. mostlyright/data_harness/readers/grib2/__init__.py +33 -0
  103. mostlyright/data_harness/readers/grib2/admission.py +722 -0
  104. mostlyright/data_harness/readers/grib2/decode.py +1009 -0
  105. mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
  106. mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
  107. mostlyright/data_harness/readers/json_tabular.py +485 -0
  108. mostlyright/data_harness/readers/registry.py +514 -0
  109. mostlyright/data_harness/readers/samples/README.md +110 -0
  110. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  111. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
  112. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  113. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
  114. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  115. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
  116. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  117. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
  118. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  119. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
  120. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  121. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
  122. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  123. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
  124. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
  125. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
  126. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
  127. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
  128. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
  129. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
  130. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
  131. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
  132. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
  133. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
  134. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  135. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
  136. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  137. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  138. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  139. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  140. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
  141. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
  142. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  143. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
  144. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  145. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
  146. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  147. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
  148. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  149. mostlyright/data_harness/readers/samples.py +582 -0
  150. mostlyright/data_harness/readers/spreadsheet.py +803 -0
  151. mostlyright/data_harness/readers/tabular.py +510 -0
  152. mostlyright/data_harness/recipe.py +5321 -0
  153. mostlyright/data_harness/repair/__init__.py +78 -0
  154. mostlyright/data_harness/repair/adapters.py +274 -0
  155. mostlyright/data_harness/repair/contracts.py +872 -0
  156. mostlyright/data_harness/repair/coordinator.py +1099 -0
  157. mostlyright/data_harness/repair/errors.py +16 -0
  158. mostlyright/data_harness/review.py +2533 -0
  159. mostlyright/data_harness/rowset.py +283 -0
  160. mostlyright/data_harness/serving.py +1975 -0
  161. mostlyright/data_harness/serving_edge.py +590 -0
  162. mostlyright/data_harness/serving_http.py +1031 -0
  163. mostlyright/data_harness/session_probes.py +759 -0
  164. mostlyright/data_harness/signing.py +101 -0
  165. mostlyright/data_harness/source_discovery.py +898 -0
  166. mostlyright/data_harness/sources/__init__.py +209 -0
  167. mostlyright/data_harness/sources/_adapter_steps.py +213 -0
  168. mostlyright/data_harness/sources/adapters.py +1214 -0
  169. mostlyright/data_harness/sources/cadence.py +1428 -0
  170. mostlyright/data_harness/sources/cadence_emission.py +453 -0
  171. mostlyright/data_harness/sources/cadence_history.py +546 -0
  172. mostlyright/data_harness/sources/catalog/__init__.py +17 -0
  173. mostlyright/data_harness/sources/catalog/admission.py +477 -0
  174. mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
  175. mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
  176. mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
  177. mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
  178. mostlyright/data_harness/sources/catalog/channel.py +523 -0
  179. mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
  180. mostlyright/data_harness/sources/catalog/contracts.py +825 -0
  181. mostlyright/data_harness/sources/catalog/coverage.py +137 -0
  182. mostlyright/data_harness/sources/catalog/delta.py +1340 -0
  183. mostlyright/data_harness/sources/catalog/embedding.py +532 -0
  184. mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
  185. mostlyright/data_harness/sources/catalog/fill.py +3889 -0
  186. mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
  187. mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
  188. mostlyright/data_harness/sources/catalog/gating.py +374 -0
  189. mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
  190. mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
  191. mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
  192. mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
  193. mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
  194. mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
  195. mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
  196. mostlyright/data_harness/sources/catalog/health.py +447 -0
  197. mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
  198. mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
  199. mostlyright/data_harness/sources/catalog/neural.py +1618 -0
  200. mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
  201. mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
  202. mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
  203. mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
  204. mostlyright/data_harness/sources/catalog/recommend.py +171 -0
  205. mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
  206. mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
  207. mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
  208. mostlyright/data_harness/sources/catalog/sealed.py +560 -0
  209. mostlyright/data_harness/sources/catalog/search.py +230 -0
  210. mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
  211. mostlyright/data_harness/sources/catalog/update.py +891 -0
  212. mostlyright/data_harness/sources/collections.py +815 -0
  213. mostlyright/data_harness/sources/contracts.py +2223 -0
  214. mostlyright/data_harness/sources/deletion.py +761 -0
  215. mostlyright/data_harness/sources/fitness.py +162 -0
  216. mostlyright/data_harness/sources/governance.py +163 -0
  217. mostlyright/data_harness/sources/hosted.py +173 -0
  218. mostlyright/data_harness/sources/integration.py +218 -0
  219. mostlyright/data_harness/sources/range_reader.py +418 -0
  220. mostlyright/data_harness/sources/registry.py +514 -0
  221. mostlyright/data_harness/sources/rights_rule.py +59 -0
  222. mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
  223. mostlyright/data_harness/sources/sports.py +521 -0
  224. mostlyright/data_harness/sources/stream.py +524 -0
  225. mostlyright/data_harness/sources/stream_connector.py +418 -0
  226. mostlyright/data_harness/sources/stream_recorder.py +1404 -0
  227. mostlyright/data_harness/studio_boundary.py +2019 -0
  228. mostlyright/data_harness/thin/__init__.py +37 -0
  229. mostlyright/data_harness/thin/acquire.py +1137 -0
  230. mostlyright/data_harness/thin/acquire_cancel.py +579 -0
  231. mostlyright/data_harness/thin/approvals.py +617 -0
  232. mostlyright/data_harness/thin/commands.py +406 -0
  233. mostlyright/data_harness/thin/download.py +194 -0
  234. mostlyright/data_harness/thin/narrative.py +589 -0
  235. mostlyright/data_harness/thin/parity.py +1070 -0
  236. mostlyright/data_harness/thin/propose.py +2759 -0
  237. mostlyright/data_harness/thin/research.py +1663 -0
  238. mostlyright/data_harness/thin/router.py +924 -0
  239. mostlyright/data_harness/thin/runs.py +519 -0
  240. mostlyright/data_harness/thin/session.py +281 -0
  241. mostlyright/data_harness/thin/stream.py +501 -0
  242. mostlyright/data_harness/thin/transport.py +187 -0
  243. mostlyright/data_harness/thin/vocabulary.py +368 -0
  244. mostlyright/data_harness/thin/workers.py +164 -0
  245. mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
  246. mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
  247. mostlyright/data_harness/unit_flow.py +927 -0
  248. mostlyright/data_harness/units.py +572 -0
  249. mostlyright/data_harness/ux/__init__.py +9 -0
  250. mostlyright/data_harness/ux/approve.py +485 -0
  251. mostlyright/data_harness/ux/author_yaml.py +597 -0
  252. mostlyright/data_harness/ux/cloud_auth.py +447 -0
  253. mostlyright/data_harness/ux/commands/__init__.py +260 -0
  254. mostlyright/data_harness/ux/commands/approve.py +136 -0
  255. mostlyright/data_harness/ux/commands/auth.py +744 -0
  256. mostlyright/data_harness/ux/commands/author.py +79 -0
  257. mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
  258. mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
  259. mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
  260. mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
  261. mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
  262. mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
  263. mostlyright/data_harness/ux/commands/deploy.py +134 -0
  264. mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
  265. mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
  266. mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
  267. mostlyright/data_harness/ux/commands/diff.py +74 -0
  268. mostlyright/data_harness/ux/commands/index.py +84 -0
  269. mostlyright/data_harness/ux/commands/inventory.py +47 -0
  270. mostlyright/data_harness/ux/commands/list_builds.py +143 -0
  271. mostlyright/data_harness/ux/commands/login.py +63 -0
  272. mostlyright/data_harness/ux/commands/peek.py +236 -0
  273. mostlyright/data_harness/ux/commands/plan_check.py +90 -0
  274. mostlyright/data_harness/ux/commands/preflight.py +97 -0
  275. mostlyright/data_harness/ux/commands/record.py +107 -0
  276. mostlyright/data_harness/ux/commands/review_setup.py +47 -0
  277. mostlyright/data_harness/ux/commands/search.py +440 -0
  278. mostlyright/data_harness/ux/commands/show.py +61 -0
  279. mostlyright/data_harness/ux/commands/whoami.py +37 -0
  280. mostlyright/data_harness/ux/credential_native.py +551 -0
  281. mostlyright/data_harness/ux/credential_store.py +1055 -0
  282. mostlyright/data_harness/ux/credentials.py +631 -0
  283. mostlyright/data_harness/ux/diffing.py +444 -0
  284. mostlyright/data_harness/ux/headline.py +671 -0
  285. mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
  286. mostlyright/data_harness/ux/hosted_run_status.py +619 -0
  287. mostlyright/data_harness/ux/inventory.py +427 -0
  288. mostlyright/data_harness/ux/local_review.py +375 -0
  289. mostlyright/data_harness/ux/login.py +691 -0
  290. mostlyright/data_harness/ux/path_kind.py +147 -0
  291. mostlyright/data_harness/ux/peek.py +1000 -0
  292. mostlyright/data_harness/ux/plain_file.py +178 -0
  293. mostlyright/data_harness/ux/plan_check.py +311 -0
  294. mostlyright/data_harness/ux/preflight.py +918 -0
  295. mostlyright/data_harness/ux/readers.py +1124 -0
  296. mostlyright/data_harness/ux/remediation.py +2195 -0
  297. mostlyright/data_harness/ux/render.py +657 -0
  298. mostlyright/data_harness/ux/workload.py +1077 -0
  299. mostlyright/data_harness/viewer.py +3713 -0
  300. mostlyright/data_harness/visual_run/__init__.py +83 -0
  301. mostlyright/data_harness/visual_run/authoring.py +235 -0
  302. mostlyright/data_harness/visual_run/contracts.py +673 -0
  303. mostlyright/data_harness/visual_run/materialize.py +486 -0
  304. mostlyright/data_harness/visual_run/observations.py +874 -0
  305. mostlyright/data_harness/visual_run/query.py +259 -0
  306. mostlyright/data_harness/visual_run/reducer.py +280 -0
  307. mostlyright/data_harness/visual_run/sdk.py +892 -0
  308. mostlyright/data_harness/visual_run/store.py +584 -0
  309. mostlyright/data_harness/visual_run/transport.py +239 -0
  310. mostlyright/data_harness/watch.py +2999 -0
  311. mostlyright_data-0.9.0.dist-info/METADATA +607 -0
  312. mostlyright_data-0.9.0.dist-info/RECORD +314 -0
  313. mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
  314. mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
@@ -0,0 +1,486 @@
1
+ """Deterministic nbformat materialization from reduced visual-run state."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import re
8
+ import secrets
9
+ import stat
10
+ import sys
11
+ from collections.abc import Callable
12
+ from ctypes import CDLL, c_char_p, c_int, get_errno
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from mostlyright.data_harness.canonical import canonical_json_bytes, sha256_bytes
17
+ from mostlyright.data_harness.visual_run.contracts import (
18
+ DOCUMENT_HEADER_CELL_ID,
19
+ LEGACY_DOCUMENT_HEADER_CELL_ID_PREFIX,
20
+ )
21
+ from mostlyright.data_harness.visual_run.observations import scrub_for_display
22
+ from mostlyright.data_harness.visual_run.reducer import CellState, ReducedRun
23
+
24
+ NOTEBOOK_FORMAT = 4
25
+ NOTEBOOK_MINOR = 5
26
+ MAX_MATERIALIZED_NOTEBOOK_BYTES = 8 * 1024 * 1024
27
+ DOCUMENT_HEADER_SOURCE = (
28
+ "# Research record\n\n"
29
+ "This document explains the source choice, dataset rules, build steps, and checks.\n"
30
+ )
31
+ _MARKDOWN_FENCE = re.compile(r"^ {0,3}(`{3,}|~{3,})\s*([^`]*)$")
32
+ _MARKDOWN_H1 = re.compile(r"^#\s+")
33
+
34
+
35
+ def _demote_legacy_h1(source: str) -> str:
36
+ """Keep historical artifact truth while projecting agent H1 headings as H2 sections."""
37
+
38
+ lines = source.splitlines(keepends=True)
39
+ fence_character: str | None = None
40
+ projected: list[str] = []
41
+ for line in lines:
42
+ body = line.rstrip("\r\n")
43
+ if fence_character is not None:
44
+ closing = re.fullmatch(rf" {{0,3}}{re.escape(fence_character)}{{3,}}\s*", body)
45
+ if closing:
46
+ fence_character = None
47
+ projected.append(line)
48
+ continue
49
+ fence = _MARKDOWN_FENCE.match(body)
50
+ if fence:
51
+ fence_character = fence.group(1)[0]
52
+ projected.append(line)
53
+ continue
54
+ projected.append("#" + line if _MARKDOWN_H1.match(body) else line)
55
+ return "".join(projected)
56
+
57
+
58
+ def _document_header_cell() -> dict[str, Any]:
59
+ """Return the fixed opening cell shared by every research notebook projection."""
60
+
61
+ return {
62
+ "cell_type": "markdown",
63
+ "id": DOCUMENT_HEADER_CELL_ID,
64
+ "metadata": {"mostlyright": {"document_header": True}},
65
+ "source": DOCUMENT_HEADER_SOURCE.splitlines(keepends=True),
66
+ }
67
+
68
+
69
+ def _artifact_output_note(stage: str) -> str:
70
+ """Describe a stored output without exposing its internal artifact identifier."""
71
+
72
+ label = " ".join(stage.replace("_", "-").split("-")).strip() or "step"
73
+ return f"{label.capitalize()} output is stored with this run.\n"
74
+
75
+
76
+ def _cell(cell: CellState, artifact_reader: Callable[[Any], bytes] | None) -> dict[str, Any]:
77
+ mostlyright: dict[str, Any] = {
78
+ "object_version": cell.version,
79
+ "phase": cell.phase,
80
+ "stage": cell.stage,
81
+ "status": cell.status,
82
+ }
83
+ if cell.status in {"writing", "executing"}:
84
+ mostlyright["activity"] = {
85
+ "schema_version": "mostlyright.research-cell-activity.v1",
86
+ "state": cell.status,
87
+ }
88
+ metadata = {"mostlyright": mostlyright}
89
+ source = []
90
+ if cell.source is not None and artifact_reader is not None:
91
+ source = artifact_reader(cell.source).decode("utf-8").splitlines(keepends=True)
92
+ if cell.cell_type == "markdown":
93
+ source = _demote_legacy_h1("".join(source)).splitlines(keepends=True)
94
+ return {
95
+ "cell_type": "markdown",
96
+ "id": cell.cell_id,
97
+ "metadata": metadata,
98
+ "source": source,
99
+ }
100
+ outputs = []
101
+ for reference in cell.outputs:
102
+ output: Any = None
103
+ if artifact_reader is not None:
104
+ try:
105
+ output = json.loads(artifact_reader(reference))
106
+ except (UnicodeDecodeError, json.JSONDecodeError):
107
+ output = None
108
+ if not isinstance(output, dict) or output.get("output_type") not in {
109
+ "stream",
110
+ "display_data",
111
+ "execute_result",
112
+ "error",
113
+ }:
114
+ output = {
115
+ "data": {
116
+ "application/vnd.mostlyright.artifact-reference.v1+json": (
117
+ reference.to_mapping()
118
+ ),
119
+ "text/plain": [_artifact_output_note(cell.stage)],
120
+ },
121
+ "metadata": {},
122
+ "output_type": "display_data",
123
+ }
124
+ outputs.append(output)
125
+ return {
126
+ "cell_type": "code",
127
+ # Jupyter's live prompt is represented by a null execution count plus the exact
128
+ # persisted ``executing`` activity. The requested count becomes visible only when
129
+ # the cell completes, so a running cell renders as ``In [*]`` rather than falsely
130
+ # claiming the execution already finished.
131
+ "execution_count": None if cell.status == "executing" else cell.execution_count,
132
+ "id": cell.cell_id,
133
+ "metadata": metadata,
134
+ "outputs": outputs,
135
+ "source": source,
136
+ }
137
+
138
+
139
+ def _observation_cell(
140
+ index: int,
141
+ reference: Any,
142
+ artifact_reader: Callable[[Any], bytes] | None,
143
+ ) -> dict[str, Any]:
144
+ value: Any = None
145
+ if artifact_reader is not None:
146
+ try:
147
+ value = json.loads(artifact_reader(reference))
148
+ except (UnicodeDecodeError, json.JSONDecodeError):
149
+ value = None
150
+ if isinstance(value, dict):
151
+ value, redacted = scrub_for_display(value)
152
+ assert isinstance(value, dict)
153
+ source_observation = (
154
+ reference.artifact_schema == "mostlyright.source-observation.v1"
155
+ and value.get("schema_version") == "mostlyright.source-observation.v1"
156
+ )
157
+ display = value.get("display", reference.display)
158
+ if not source_observation and display in {
159
+ "aggregate_only",
160
+ "schema_only",
161
+ "summary",
162
+ "redacted",
163
+ }:
164
+ value["sample"] = []
165
+ value["operation_parameters"] = {}
166
+ value["source_coordinates"] = []
167
+ coverage = value.get("coverage")
168
+ if isinstance(coverage, dict):
169
+ coverage["representative_window"] = None
170
+ if redacted and not source_observation:
171
+ value["display"] = "schema_only"
172
+ value["sample"] = []
173
+ value["operation_parameters"] = {}
174
+ value["source_coordinates"] = []
175
+ coverage = value.get("coverage")
176
+ if isinstance(coverage, dict):
177
+ coverage["representative_window"] = None
178
+ mime = (
179
+ "application/vnd.mostlyright.source-observation+json"
180
+ if source_observation
181
+ else "application/vnd.mostlyright.stage-observation+json"
182
+ )
183
+ label = "source" if source_observation else value.get("operation", "stage")
184
+ rows = value.get("row_count") if source_observation else value.get("output_rows")
185
+ data = {
186
+ mime: value,
187
+ "text/plain": [
188
+ f"Mostly Right {label} observation "
189
+ f"({rows if rows is not None else 'unknown'} rows)\n"
190
+ ],
191
+ }
192
+ else:
193
+ data = {
194
+ "application/vnd.mostlyright.artifact-reference.v1+json": {
195
+ "sha256": reference.sha256,
196
+ "display": reference.display,
197
+ },
198
+ "text/plain": ["Mostly Right observation artifact\n"],
199
+ }
200
+ return {
201
+ "cell_type": "code",
202
+ "execution_count": None,
203
+ "id": f"mr-observation-{index:04d}-{reference.sha256[:24]}",
204
+ "metadata": {
205
+ "mostlyright": {
206
+ "artifact_sha256": reference.sha256,
207
+ "passive": True,
208
+ "schema_version": "mostlyright.passive-observation-cell.v1",
209
+ }
210
+ },
211
+ "outputs": [{"data": data, "metadata": {}, "output_type": "display_data"}],
212
+ "source": [],
213
+ }
214
+
215
+
216
+ def notebook_value(
217
+ state: ReducedRun, artifact_reader: Callable[[Any], bytes] | None = None
218
+ ) -> dict[str, Any]:
219
+ if any(key != cell.cell_id for key, cell in state.cells.items()):
220
+ raise ValueError("visual-run state has a cell id that differs from its mapping key")
221
+ # Refuse before calling an artifact reader: references are durable, authenticated size facts,
222
+ # so they provide the only way to prevent a huge source/output from being allocated first and
223
+ # rejected only after serialization.
224
+ artifact_bytes = sum(
225
+ reference.byte_size
226
+ for cell in state.cells.values()
227
+ for reference in ((cell.source,) if cell.source is not None else ()) + cell.outputs
228
+ ) + sum(reference.byte_size for reference in state.observations)
229
+ if artifact_bytes > MAX_MATERIALIZED_NOTEBOOK_BYTES:
230
+ raise ValueError("visual-run artifacts exceed the materialized notebook byte limit")
231
+ authored_ids = {cell.cell_id for cell in state.cells.values()}
232
+ legacy_header_id = LEGACY_DOCUMENT_HEADER_CELL_ID_PREFIX
233
+ suffix = 2
234
+ while legacy_header_id in authored_ids:
235
+ legacy_header_id = f"{LEGACY_DOCUMENT_HEADER_CELL_ID_PREFIX}-{suffix}"
236
+ suffix += 1
237
+ cells = [_document_header_cell()]
238
+ for cell in state.cells.values():
239
+ projected = _cell(cell, artifact_reader)
240
+ if projected["id"] == DOCUMENT_HEADER_CELL_ID:
241
+ projected["id"] = legacy_header_id
242
+ cells.append(projected)
243
+ cells.extend(
244
+ _observation_cell(index, reference, artifact_reader)
245
+ for index, reference in enumerate(state.observations, 1)
246
+ )
247
+ return {
248
+ "cells": cells,
249
+ "metadata": {
250
+ "kernelspec": {
251
+ "display_name": "Python 3",
252
+ "language": "python",
253
+ "name": "python3",
254
+ },
255
+ "language_info": {"name": "python", "version": "3.13"},
256
+ "mostlyright": {
257
+ "cursor": state.cursor,
258
+ "lifecycle": state.lifecycle,
259
+ "run_id": state.run_id,
260
+ "schema_version": "mostlyright.research-notebook.v2",
261
+ },
262
+ },
263
+ "nbformat": NOTEBOOK_FORMAT,
264
+ "nbformat_minor": NOTEBOOK_MINOR,
265
+ }
266
+
267
+
268
+ def notebook_bytes(
269
+ state: ReducedRun, artifact_reader: Callable[[Any], bytes] | None = None
270
+ ) -> bytes:
271
+ content = canonical_json_bytes(notebook_value(state, artifact_reader))
272
+ if len(content) > MAX_MATERIALIZED_NOTEBOOK_BYTES:
273
+ raise ValueError("generated notebook exceeds the visual materializer byte limit")
274
+ return content
275
+
276
+
277
+ def notebook_digest(
278
+ state: ReducedRun, artifact_reader: Callable[[Any], bytes] | None = None
279
+ ) -> str:
280
+ return sha256_bytes(notebook_bytes(state, artifact_reader))
281
+
282
+
283
+ def materialize_notebook(
284
+ path: Path,
285
+ state: ReducedRun,
286
+ artifact_reader: Callable[[Any], bytes] | None = None,
287
+ *,
288
+ directory_fd: int | None = None,
289
+ ) -> str:
290
+ path = Path(path)
291
+ content = notebook_bytes(state, artifact_reader)
292
+ owned_directory = directory_fd is None
293
+ if directory_fd is None:
294
+ path.parent.mkdir(parents=True, exist_ok=True)
295
+ directory_fd = os.open(
296
+ path.parent,
297
+ os.O_RDONLY | os.O_DIRECTORY | getattr(os, "O_NOFOLLOW", 0),
298
+ )
299
+ try:
300
+ existing_raw, existing_identity = _read_existing(directory_fd, path.name)
301
+ if existing_raw is not None:
302
+ try:
303
+ existing = json.loads(existing_raw)
304
+ except (UnicodeError, json.JSONDecodeError) as exc:
305
+ raise ValueError("existing notebook is not a valid derived notebook") from exc
306
+ mostlyright = existing.get("metadata", {}).get("mostlyright", {})
307
+ marker = mostlyright.get("schema_version")
308
+ seeded = mostlyright.get("phase") == "ready_for_research" and all(
309
+ cell.get("metadata", {}).get("mostlyright", {}).get("seed") is True
310
+ for cell in existing.get("cells", ())
311
+ )
312
+ if marker != "mostlyright.research-notebook.v2" and not seeded:
313
+ raise ValueError(
314
+ "refusing to replace a notebook not owned by the visual materializer"
315
+ )
316
+ if existing_raw == content:
317
+ return sha256_bytes(content)
318
+ temporary = f".{path.name}.{secrets.token_hex(12)}.tmp"
319
+ descriptor = os.open(
320
+ temporary,
321
+ os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_CLOEXEC | os.O_NOFOLLOW,
322
+ 0o600,
323
+ dir_fd=directory_fd,
324
+ )
325
+ try:
326
+ written = 0
327
+ while written < len(content):
328
+ progress = os.write(descriptor, content[written:])
329
+ if progress <= 0:
330
+ raise OSError("notebook write made no progress")
331
+ written += progress
332
+ os.fsync(descriptor)
333
+ finally:
334
+ os.close(descriptor)
335
+ _install_if_unchanged(
336
+ directory_fd,
337
+ temporary,
338
+ path.name,
339
+ existing_identity,
340
+ sha256_bytes(existing_raw) if existing_raw is not None else None,
341
+ )
342
+ os.fsync(directory_fd)
343
+ return sha256_bytes(content)
344
+ finally:
345
+ if owned_directory:
346
+ os.close(directory_fd)
347
+
348
+
349
+ def _file_identity(observed: os.stat_result) -> tuple[int, int, int, int, int, int, int]:
350
+ return (
351
+ observed.st_dev,
352
+ observed.st_ino,
353
+ observed.st_mode,
354
+ observed.st_nlink,
355
+ observed.st_size,
356
+ observed.st_mtime_ns,
357
+ observed.st_ctime_ns,
358
+ )
359
+
360
+
361
+ def _read_existing(
362
+ directory_fd: int, name: str
363
+ ) -> tuple[bytes | None, tuple[int, int, int, int, int, int, int] | None]:
364
+ try:
365
+ descriptor = os.open(
366
+ name,
367
+ os.O_RDONLY | os.O_CLOEXEC | os.O_NOFOLLOW,
368
+ dir_fd=directory_fd,
369
+ )
370
+ except FileNotFoundError:
371
+ return None, None
372
+ try:
373
+ observed = os.fstat(descriptor)
374
+ if not stat.S_ISREG(observed.st_mode) or observed.st_nlink != 1:
375
+ raise ValueError("existing notebook must be a regular single-link file")
376
+ if observed.st_size > MAX_MATERIALIZED_NOTEBOOK_BYTES:
377
+ raise ValueError("existing notebook exceeds the visual materializer byte limit")
378
+ chunks: list[bytes] = []
379
+ remaining = MAX_MATERIALIZED_NOTEBOOK_BYTES + 1
380
+ while remaining and (chunk := os.read(descriptor, min(64 * 1024, remaining))):
381
+ chunks.append(chunk)
382
+ remaining -= len(chunk)
383
+ raw = b"".join(chunks)
384
+ after = os.fstat(descriptor)
385
+ if len(raw) > MAX_MATERIALIZED_NOTEBOOK_BYTES or _file_identity(after) != _file_identity(
386
+ observed
387
+ ):
388
+ raise ValueError("existing notebook changed while it was being read")
389
+ return raw, _file_identity(observed)
390
+ finally:
391
+ os.close(descriptor)
392
+
393
+
394
+ def _require_destination_identity(
395
+ directory_fd: int,
396
+ name: str,
397
+ expected: tuple[int, int, int, int, int, int, int] | None,
398
+ expected_sha256: str | None,
399
+ ) -> None:
400
+ observed_raw, observed_identity = _read_existing(directory_fd, name)
401
+ # Linux updates ctime during RENAME_EXCHANGE, so it cannot be compared after the atomic swap.
402
+ # Device/inode/mode/link identity plus the exact bounded content digest proves that the
403
+ # displaced object is the one we admitted, even if an attacker tried to spoof timestamps.
404
+ if (
405
+ observed_identity is None
406
+ or expected is None
407
+ or observed_identity[:4] != expected[:4]
408
+ or observed_raw is None
409
+ or expected_sha256 is None
410
+ or sha256_bytes(observed_raw) != expected_sha256
411
+ ):
412
+ raise ValueError("existing notebook changed before atomic materialization")
413
+
414
+
415
+ def _rename_with_flags(directory_fd: int, source: str, destination: str, flag: int) -> None:
416
+ libc = CDLL(None, use_errno=True)
417
+ encoded_source = os.fsencode(source)
418
+ encoded_destination = os.fsencode(destination)
419
+ if sys.platform == "darwin":
420
+ result = libc.renameatx_np(
421
+ c_int(directory_fd),
422
+ c_char_p(encoded_source),
423
+ c_int(directory_fd),
424
+ c_char_p(encoded_destination),
425
+ c_int(flag),
426
+ )
427
+ else:
428
+ result = libc.renameat2(
429
+ c_int(directory_fd),
430
+ c_char_p(encoded_source),
431
+ c_int(directory_fd),
432
+ c_char_p(encoded_destination),
433
+ c_int(flag),
434
+ )
435
+ if result != 0:
436
+ error = get_errno()
437
+ raise OSError(error, os.strerror(error), destination)
438
+
439
+
440
+ def _install_if_unchanged(
441
+ directory_fd: int,
442
+ temporary: str,
443
+ destination: str,
444
+ expected: tuple[int, int, int, int, int, int, int] | None,
445
+ expected_sha256: str | None,
446
+ ) -> None:
447
+ """Atomically install without ever overwriting an unchecked destination object."""
448
+
449
+ if expected is None:
450
+ # Darwin RENAME_EXCL and Linux RENAME_NOREPLACE have different flag values.
451
+ try:
452
+ _rename_with_flags(
453
+ directory_fd,
454
+ temporary,
455
+ destination,
456
+ 0x00000004 if sys.platform == "darwin" else 1,
457
+ )
458
+ except BaseException:
459
+ # A failed no-replace leaves our generated file at the temporary name.
460
+ os.unlink(temporary, dir_fd=directory_fd)
461
+ raise
462
+ return
463
+ # Exchange is the only portable kernel primitive that prevents a substitution between a
464
+ # userspace identity check and installation. The displaced object remains recoverable under
465
+ # our random temporary name until its identity is proven.
466
+ try:
467
+ _rename_with_flags(directory_fd, temporary, destination, 0x00000002)
468
+ except BaseException:
469
+ # A failed exchange leaves our generated file at the temporary name.
470
+ os.unlink(temporary, dir_fd=directory_fd)
471
+ raise
472
+ try:
473
+ _require_destination_identity(directory_fd, temporary, expected, expected_sha256)
474
+ except BaseException as identity_error:
475
+ # Restore both names atomically; never unlink the displaced, unchecked object.
476
+ try:
477
+ _rename_with_flags(directory_fd, temporary, destination, 0x00000002)
478
+ except BaseException as rollback_error:
479
+ # Both objects remain recoverable. In particular, the unchecked displaced object is
480
+ # never deleted merely because restoration encountered an operating-system failure.
481
+ raise RuntimeError(
482
+ "atomic notebook replacement could not be restored"
483
+ ) from rollback_error
484
+ os.unlink(temporary, dir_fd=directory_fd)
485
+ raise identity_error
486
+ os.unlink(temporary, dir_fd=directory_fd)