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,576 @@
1
+ """Plain, bounded presentation of run-feed records.
2
+
3
+ The event feed is the authority for what happened. This module supplies the one presentation
4
+ vocabulary shared by Watch and the Notebook Viewer, plus a pure reducer for the Viewer's compact
5
+ Build block. It opens no files, reads no clocks, and never re-derives records from receipts.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Mapping, Sequence
11
+ from typing import Any
12
+
13
+ from mostlyright.data_harness.events import NOTEBOOK_SIDECAR_EVENT_NAMES, validate_facts
14
+
15
+ # The rail is a plain-vocabulary rendering of the build and evidence stages declared by
16
+ # ``cli._agent_workflow_surface()``. Presentation-only observability is intentionally absent. A
17
+ # test owns the one-way subset contract because importing ``cli`` here would close a cycle.
18
+ STAGE_RAIL: tuple[Mapping[str, Any], ...] = (
19
+ {
20
+ "stage": "question_and_requirements",
21
+ "label": "Question and requirements",
22
+ "events": (),
23
+ "members": ("question.json", "requirements.json"),
24
+ "steps": (),
25
+ },
26
+ {
27
+ "stage": "source_candidates_and_fitness",
28
+ "label": "Sources proposed",
29
+ "events": (),
30
+ "members": ("source-proposals.json",),
31
+ "steps": (),
32
+ },
33
+ {
34
+ "stage": "frozen_recipe",
35
+ "label": "Recipe written",
36
+ "events": (),
37
+ "members": ("recipe.json",),
38
+ "steps": (),
39
+ },
40
+ {
41
+ "stage": "recipe_approval",
42
+ "label": "Recipe approved",
43
+ "events": (),
44
+ "members": ("recipe-approval.json",),
45
+ "steps": (),
46
+ },
47
+ {
48
+ "stage": "acquire_clean_join_validate_package",
49
+ "label": "Build",
50
+ "events": ("run_started", "rights_checked", "build_failed", "run_interrupted"),
51
+ "steps": (
52
+ {"step": "sources_read", "label": "Sources read", "events": ("source_read",)},
53
+ {
54
+ "step": "sources_opened",
55
+ "label": "Sources opened",
56
+ "events": ("source_parsed",),
57
+ },
58
+ {
59
+ "step": "cleaning_applied",
60
+ "label": "Cleaning applied",
61
+ "events": ("cleaning_applied",),
62
+ },
63
+ {"step": "joined", "label": "Joined", "events": ("join_completed",)},
64
+ {"step": "checks", "label": "Checks", "events": ("checks_completed",)},
65
+ {
66
+ "step": "table_written",
67
+ "label": "Table written",
68
+ "events": ("table_written",),
69
+ },
70
+ {
71
+ "step": "receipt_sealed",
72
+ "label": "Receipt sealed",
73
+ "events": ("member_sealed", "build_sealed"),
74
+ },
75
+ ),
76
+ },
77
+ {"stage": "independent_review", "label": "Review", "events": (), "steps": ()},
78
+ {
79
+ "stage": "independent_verification",
80
+ "label": "Independent check",
81
+ "events": (),
82
+ "steps": (),
83
+ },
84
+ {
85
+ "stage": "handoff",
86
+ "label": "Handoff",
87
+ "events": ("notebook_sidecar_ready",),
88
+ "steps": (),
89
+ },
90
+ {"stage": "hosted_export", "label": "Hosted export", "events": (), "steps": ()},
91
+ )
92
+
93
+
94
+ # One short sentence per event name. Internal event names never reach either page.
95
+ EVENT_SENTENCES: Mapping[str, str] = {
96
+ "run_started": "Build started",
97
+ "rights_checked": "Rights checked",
98
+ "sources_read_started": "Started reading {total} sources",
99
+ "source_read": "Read {source_id} ({bytes} bytes)",
100
+ "sources_parsed_started": "Started opening {total} sources",
101
+ "source_parsed": "Opened {source_id}: {rows} rows, {columns} columns",
102
+ "evidence_matched": "Sources matched their proposals: {matched} of {total}",
103
+ "cleaning_started": "Started {total} cleaning steps",
104
+ "cleaning_applied": "Cleaning step {step_index} of {step_total} on {source_id}: {operation}",
105
+ "sources_profiled": "Cleaned sources profiled: {sources} sources, {columns} columns",
106
+ "join_completed": "Joined: {matched_left_rows} of {left_rows} rows matched",
107
+ "rows_selected": "Rows selected: {rows} rows, {columns} columns",
108
+ "checks_started": "Started {total} output checks",
109
+ "check_completed": "Check {index} of {total}: {check_id}",
110
+ "checks_completed": "Checks: {passed} of {total} passed",
111
+ "table_written": "Table written: {rows} rows, {columns} columns",
112
+ "members_sealed_started": "Started sealing {total} receipt parts",
113
+ "member_sealed": "Receipt part sealed ({bytes} bytes)",
114
+ "candidate_installed": "Build installed: {members} parts, {bytes} bytes",
115
+ "snapshot_verify_started": "Started checking {total} receipt parts",
116
+ "member_verified": "Receipt part checked ({bytes} bytes)",
117
+ "snapshot_verified": "Build checked: {members} parts match",
118
+ "build_sealed": "Receipt sealed",
119
+ "build_failed": "Build stopped: {message}",
120
+ "run_interrupted": "Build interrupted",
121
+ "notebook_sidecar_started": "Generating table notebook",
122
+ "notebook_sidecar_ready": "Table notebook ready",
123
+ "notebook_sidecar_failed": "Table notebook could not be rendered ({code})",
124
+ }
125
+
126
+ STOPPED_EVENTS = frozenset({"build_failed", "run_interrupted"})
127
+
128
+
129
+ # A fixed span vocabulary makes the rendered block bounded independently of source, check, and
130
+ # receipt-member counts. Per-unit records update one existing line; they never create another line.
131
+ BUILD_SPANS: tuple[Mapping[str, Any], ...] = (
132
+ {
133
+ "key": "prepare",
134
+ "label": "Start the Build",
135
+ "complete": "run_started",
136
+ },
137
+ {"key": "rights", "label": "Check source rights", "complete": "rights_checked"},
138
+ {
139
+ "key": "read",
140
+ "label": "Read source bytes",
141
+ "start": "sources_read_started",
142
+ "unit": "source_read",
143
+ "unique": "source_id",
144
+ "noun": "sources read",
145
+ },
146
+ {
147
+ "key": "open",
148
+ "label": "Open source data",
149
+ "start": "sources_parsed_started",
150
+ "unit": "source_parsed",
151
+ "unique": "source_id",
152
+ "noun": "sources opened",
153
+ },
154
+ {"key": "match", "label": "Match source evidence", "complete": "evidence_matched"},
155
+ {
156
+ "key": "clean",
157
+ "label": "Apply declared cleaning",
158
+ "start": "cleaning_started",
159
+ "unit": "cleaning_applied",
160
+ "unique": "step_index",
161
+ "noun": "steps applied",
162
+ },
163
+ {"key": "profile", "label": "Profile cleaned sources", "complete": "sources_profiled"},
164
+ {"key": "join", "label": "Join sources", "complete": "join_completed"},
165
+ {"key": "select", "label": "Select dataset rows", "complete": "rows_selected"},
166
+ {
167
+ "key": "checks",
168
+ "label": "Run output checks",
169
+ "start": "checks_started",
170
+ "unit": "check_completed",
171
+ "unique": "index",
172
+ "complete": "checks_completed",
173
+ "noun": "checks run",
174
+ },
175
+ {"key": "write", "label": "Write the table", "complete": "table_written"},
176
+ {
177
+ "key": "seal",
178
+ "label": "Seal receipt parts",
179
+ "start": "members_sealed_started",
180
+ "unit": "member_sealed",
181
+ "unique": "evidence.member",
182
+ "noun": "parts sealed",
183
+ },
184
+ {"key": "install", "label": "Install the Build", "complete": "candidate_installed"},
185
+ {
186
+ "key": "verify",
187
+ "label": "Check the installed Build",
188
+ "start": "snapshot_verify_started",
189
+ "unit": "member_verified",
190
+ "unique": "evidence.member",
191
+ "complete": "snapshot_verified",
192
+ "noun": "parts checked",
193
+ },
194
+ {"key": "finish", "label": "Finish the Build", "complete": "build_sealed"},
195
+ )
196
+
197
+
198
+ def sentence(event: str, facts: Mapping[str, Any]) -> str:
199
+ """Render one event in plain words, retaining a safe fallback for producer drift."""
200
+
201
+ template = EVENT_SENTENCES.get(event)
202
+ if template is None:
203
+ return event.replace("_", " ")
204
+ try:
205
+ return template.format(**facts)
206
+ except (KeyError, IndexError, ValueError, TypeError):
207
+ return event.replace("_", " ")
208
+
209
+
210
+ def _facts(record: Mapping[str, Any]) -> Mapping[str, Any]:
211
+ value = record.get("facts")
212
+ return value if isinstance(value, Mapping) else {}
213
+
214
+
215
+ def _moment(record: Mapping[str, Any]) -> float | None:
216
+ value = record.get("at")
217
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
218
+ return float(value)
219
+ return None
220
+
221
+
222
+ def _unique_value(record: Mapping[str, Any], key: str) -> object | None:
223
+ if key == "evidence.member":
224
+ evidence = record.get("evidence")
225
+ return evidence.get("member") if isinstance(evidence, Mapping) else None
226
+ return _facts(record).get(key)
227
+
228
+
229
+ def _sequence(record: Mapping[str, Any]) -> int | None:
230
+ value = record.get("seq")
231
+ if isinstance(value, int) and not isinstance(value, bool) and value > 0:
232
+ return value
233
+ return None
234
+
235
+
236
+ def _valid_sidecar_record(
237
+ record: Mapping[str, Any],
238
+ event: str,
239
+ *,
240
+ previous: Mapping[str, Any],
241
+ ) -> bool:
242
+ """Admit one schema-valid, evidence-free record at the next fixed sequence position."""
243
+
244
+ if (
245
+ record.get("event") != event
246
+ or "evidence" not in record
247
+ or record.get("evidence") is not None
248
+ ):
249
+ return False
250
+ facts = record.get("facts")
251
+ try:
252
+ validate_facts(event, facts) # type: ignore[arg-type]
253
+ except ValueError:
254
+ return False
255
+ sequence = _sequence(record)
256
+ previous_sequence = _sequence(previous)
257
+ moment = _moment(record)
258
+ previous_moment = _moment(previous)
259
+ return (
260
+ sequence is not None
261
+ and previous_sequence is not None
262
+ and sequence == previous_sequence + 1
263
+ and moment is not None
264
+ and previous_moment is not None
265
+ and moment >= previous_moment
266
+ )
267
+
268
+
269
+ def _latest_attempt(records: Sequence[Mapping[str, Any]]) -> list[Mapping[str, Any]]:
270
+ """Use the latest narrated run if malformed bytes contain two starts in one attempt."""
271
+
272
+ start = 0
273
+ for index, record in enumerate(records):
274
+ if record.get("event") == "run_started":
275
+ start = index
276
+ return list(records[start:])
277
+
278
+
279
+ def coherent_build_seal_candidate(records: Sequence[Mapping[str, Any]]) -> str | None:
280
+ """Return a selected attempt's schema-valid, evidence-free terminal candidate digest."""
281
+
282
+ selected = _latest_attempt(records)
283
+ terminal = next(
284
+ (record for record in selected if record.get("event") in {"build_sealed", *STOPPED_EVENTS}),
285
+ None,
286
+ )
287
+ if terminal is None or terminal.get("event") != "build_sealed":
288
+ return None
289
+ if "evidence" not in terminal or terminal.get("evidence") is not None:
290
+ return None
291
+ facts = terminal.get("facts")
292
+ try:
293
+ validate_facts("build_sealed", facts) # type: ignore[arg-type]
294
+ except ValueError:
295
+ return None
296
+ digest = facts.get("candidate_digest") if isinstance(facts, Mapping) else None
297
+ return digest if isinstance(digest, str) else None
298
+
299
+
300
+ def reduce_lifecycle(
301
+ records: Sequence[Mapping[str, Any]],
302
+ *,
303
+ table_ready: bool,
304
+ allow_legacy_handoff: bool = True,
305
+ ) -> dict[str, Any]:
306
+ """Reduce real feed records to one bounded, JSON-ready lifecycle snapshot.
307
+
308
+ ``table_ready`` is the Viewer's independent verified-sidecar admission result. It authorizes
309
+ handoff; the untrusted feed narrates the Build but never authorizes access to dataset bytes.
310
+ """
311
+
312
+ bounded = list(records)
313
+ post_seal: list[Mapping[str, Any]] = []
314
+ following: list[Mapping[str, Any]] = []
315
+ sidecar_narrated = False
316
+ first_terminal = next(
317
+ (
318
+ index
319
+ for index, record in enumerate(bounded)
320
+ if record.get("event") in {"build_sealed", *STOPPED_EVENTS}
321
+ ),
322
+ None,
323
+ )
324
+ if first_terminal is not None:
325
+ terminal_prefix = bounded[: first_terminal + 1]
326
+ terminal_name = bounded[first_terminal].get("event")
327
+ if terminal_name == "build_sealed":
328
+ following = bounded[first_terminal + 1 :]
329
+ sidecar_narrated = any(
330
+ record.get("event") in NOTEBOOK_SIDECAR_EVENT_NAMES for record in following
331
+ )
332
+ # The sidecar tail is a fixed state machine, not an arbitrary post-seal extension. A
333
+ # forged or malformed ordering cannot claim readiness: facts must match the declared
334
+ # event schema, evidence must be absent, and sequence/time must grow exactly once from
335
+ # the seal through started -> ready|failed. The first valid outcome is terminal; no
336
+ # record after it is considered part of this attempt.
337
+ if following and _valid_sidecar_record(
338
+ following[0], "notebook_sidecar_started", previous=terminal_prefix[-1]
339
+ ):
340
+ post_seal.append(following[0])
341
+ if len(following) > 1:
342
+ outcome = following[1]
343
+ outcome_name = outcome.get("event")
344
+ if outcome_name in NOTEBOOK_SIDECAR_EVENT_NAMES - {
345
+ "notebook_sidecar_started"
346
+ } and _valid_sidecar_record(
347
+ outcome,
348
+ str(outcome_name),
349
+ previous=post_seal[0],
350
+ ):
351
+ post_seal.append(outcome)
352
+ bounded = [*terminal_prefix, *post_seal]
353
+ selected = _latest_attempt(bounded) if bounded else []
354
+ if not selected:
355
+ return {
356
+ "status": "completed" if table_ready else "idle",
357
+ "phase": "Table notebook ready" if table_ready else "Research notebook",
358
+ "progress": 100 if table_ready else 0,
359
+ "started_at": None,
360
+ "ended_at": None,
361
+ "sealed": table_ready,
362
+ "handoff": table_ready,
363
+ "lines": [],
364
+ "terminal": None,
365
+ "sidecar_narrated": False,
366
+ "sidecar_state": "absent",
367
+ "legacy_handoff_allowed": allow_legacy_handoff,
368
+ }
369
+
370
+ by_name: dict[str, list[Mapping[str, Any]]] = {}
371
+ for record in selected:
372
+ name = record.get("event")
373
+ if isinstance(name, str):
374
+ by_name.setdefault(name, []).append(record)
375
+
376
+ terminal_record = next(
377
+ (record for record in selected if record.get("event") in {"build_sealed", *STOPPED_EVENTS}),
378
+ None,
379
+ )
380
+ terminal_name = terminal_record.get("event") if terminal_record is not None else None
381
+ narrated_seal = coherent_build_seal_candidate(selected) is not None
382
+ sealed = narrated_seal
383
+ stopped = terminal_name in STOPPED_EVENTS
384
+ sidecar_started = bool(post_seal)
385
+ sidecar_outcome = post_seal[-1].get("event") if post_seal else None
386
+ sidecar_ready = sidecar_outcome == "notebook_sidecar_ready"
387
+ sidecar_failed = sidecar_outcome == "notebook_sidecar_failed"
388
+ if not sidecar_narrated:
389
+ sidecar_state = "absent"
390
+ elif not post_seal:
391
+ sidecar_state = "invalid"
392
+ elif len(post_seal) == 1 and len(following) > 1:
393
+ sidecar_state = "invalid"
394
+ elif sidecar_ready:
395
+ sidecar_state = "ready"
396
+ elif sidecar_failed:
397
+ sidecar_state = "failed"
398
+ else:
399
+ sidecar_state = "started"
400
+ # Existing sealed feeds predate sidecar narration and retain their independently admitted
401
+ # handoff. A current CLI attempt announces ``started`` before touching the path, so it cannot
402
+ # hand off on file timing alone: both its real ``ready`` record and independent admission are
403
+ # required.
404
+ # Only a feed with no sidecar names at all is legacy. A malformed, evidence-bearing, or
405
+ # out-of-sequence sidecar tail is current narration that failed validation, not permission to
406
+ # fall back to legacy handoff.
407
+ handoff = (
408
+ narrated_seal
409
+ and table_ready
410
+ and (sidecar_ready or (allow_legacy_handoff and not sidecar_narrated))
411
+ )
412
+
413
+ lines: list[dict[str, str]] = []
414
+ completed = 0
415
+ for span in BUILD_SPANS:
416
+ start_name = span.get("start")
417
+ complete_name = span.get("complete")
418
+ unit_name = span.get("unit")
419
+ starts = by_name.get(str(start_name), []) if start_name else []
420
+ completions = by_name.get(str(complete_name), []) if complete_name else []
421
+ units = by_name.get(str(unit_name), []) if unit_name else []
422
+ if not starts and not completions and not units:
423
+ continue
424
+
425
+ is_done = bool(completions)
426
+ detail_record = completions[-1] if completions else units[-1] if units else starts[-1]
427
+ detail = sentence(str(detail_record.get("event", "")), _facts(detail_record))
428
+ if unit_name and starts:
429
+ total_value = _facts(starts[-1]).get("total")
430
+ total = (
431
+ total_value
432
+ if isinstance(total_value, int)
433
+ and not isinstance(total_value, bool)
434
+ and total_value >= 0
435
+ else None
436
+ )
437
+ unique_key = str(span.get("unique", ""))
438
+ values: set[object] = set()
439
+ for record in units:
440
+ value = _unique_value(record, unique_key)
441
+ if value is None:
442
+ continue
443
+ try:
444
+ hash(value)
445
+ except TypeError:
446
+ # Reader-side fact drift must not make the live viewer fail to start.
447
+ continue
448
+ values.add(value)
449
+ done = len(values)
450
+ if total is not None:
451
+ done = min(done, total)
452
+ is_done = is_done or done >= total
453
+ detail = f"{done} of {total} {span['noun']}"
454
+ if is_done:
455
+ completed += 1
456
+ lines.append(
457
+ {
458
+ "key": str(span["key"]),
459
+ "label": str(span["label"]),
460
+ "status": "done" if is_done else "running",
461
+ "detail": detail,
462
+ }
463
+ )
464
+
465
+ if stopped and terminal_record is not None:
466
+ lines.append(
467
+ {
468
+ "key": "stopped",
469
+ "label": "Build stopped",
470
+ "status": "failed",
471
+ "detail": sentence(str(terminal_name), _facts(terminal_record)),
472
+ }
473
+ )
474
+
475
+ if post_seal:
476
+ sidecar_record = post_seal[-1]
477
+ sidecar_name = str(sidecar_record.get("event", ""))
478
+ sidecar_status = "failed" if sidecar_failed else "done" if sidecar_ready else "running"
479
+ lines.append(
480
+ {
481
+ "key": "table-notebook",
482
+ "label": "Prepare table notebook",
483
+ "status": sidecar_status,
484
+ "detail": sentence(sidecar_name, _facts(sidecar_record)),
485
+ }
486
+ )
487
+
488
+ started_record = by_name.get("run_started", [selected[0]])[0]
489
+ started_at = _moment(started_record)
490
+ ended_at = _moment(terminal_record) if terminal_record is not None else None
491
+ latest = selected[-1]
492
+ latest_name = str(latest.get("event", ""))
493
+ if handoff:
494
+ status = "completed"
495
+ phase = "Build complete · table notebook ready"
496
+ elif sidecar_failed:
497
+ status = "completed"
498
+ phase = "Build complete · table notebook unavailable"
499
+ elif sidecar_ready:
500
+ status = "completed"
501
+ phase = "Build complete · checking table notebook"
502
+ elif sidecar_started:
503
+ status = "completed"
504
+ phase = "Build complete · generating table notebook"
505
+ elif sealed:
506
+ status = "completed"
507
+ phase = "Build complete · preparing table notebook"
508
+ elif stopped:
509
+ status = "failed"
510
+ phase = sentence(str(terminal_name), _facts(terminal_record or {}))
511
+ else:
512
+ status = "running"
513
+ phase = sentence(latest_name, _facts(latest))
514
+ progress = 100 if sealed else round(100 * completed / len(BUILD_SPANS))
515
+ terminal: dict[str, str | None] | None = None
516
+ if stopped and terminal_record is not None:
517
+ terminal_facts = _facts(terminal_record)
518
+ finding_id = terminal_facts.get("finding_id")
519
+ severity = terminal_facts.get("severity")
520
+ terminal = {
521
+ "event": str(terminal_name),
522
+ "title": "Build interrupted" if terminal_name == "run_interrupted" else "Build stopped",
523
+ "detail": sentence(str(terminal_name), terminal_facts),
524
+ "finding_id": finding_id if isinstance(finding_id, str) else None,
525
+ "severity": severity if isinstance(severity, str) else None,
526
+ }
527
+ return {
528
+ "status": status,
529
+ "phase": phase,
530
+ "progress": progress,
531
+ "started_at": started_at,
532
+ "ended_at": ended_at,
533
+ "sealed": sealed,
534
+ "handoff": handoff,
535
+ "lines": lines,
536
+ "terminal": terminal,
537
+ "sidecar_narrated": sidecar_narrated,
538
+ "sidecar_state": sidecar_state,
539
+ "legacy_handoff_allowed": allow_legacy_handoff,
540
+ }
541
+
542
+
543
+ def actual_run_started_record(
544
+ records: Sequence[Mapping[str, Any]],
545
+ ) -> Mapping[str, Any] | None:
546
+ """Return the selected attempt's real ``run_started`` record, never a fallback record."""
547
+
548
+ bounded: list[Mapping[str, Any]] = []
549
+ for record in records:
550
+ bounded.append(record)
551
+ if record.get("event") in {"build_sealed", *STOPPED_EVENTS}:
552
+ break
553
+ selected = _latest_attempt(bounded) if bounded else []
554
+ starts = [record for record in selected if record.get("event") == "run_started"]
555
+ return starts[0] if starts else None
556
+
557
+
558
+ def elapsed_seconds(lifecycle: Mapping[str, Any], *, now: float) -> int | None:
559
+ """Return a non-negative whole-second run clock from lifecycle timestamps."""
560
+
561
+ started = lifecycle.get("started_at")
562
+ if not isinstance(started, (int, float)) or isinstance(started, bool):
563
+ return None
564
+ ended = lifecycle.get("ended_at")
565
+ stop = float(ended) if isinstance(ended, (int, float)) and not isinstance(ended, bool) else now
566
+ return max(0, int(stop - float(started)))
567
+
568
+
569
+ def format_elapsed(seconds: int | None) -> str:
570
+ """Format the run clock without locale or wall-clock dependencies."""
571
+
572
+ if seconds is None:
573
+ return ""
574
+ hours, remainder = divmod(seconds, 3600)
575
+ minutes, secs = divmod(remainder, 60)
576
+ return f"{hours:d}:{minutes:02d}:{secs:02d}" if hours else f"{minutes:02d}:{secs:02d}"