mostlyright-data 0.9.6__tar.gz → 0.9.7__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 (326) hide show
  1. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/PKG-INFO +1 -1
  2. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/pyproject.toml +1 -1
  3. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/sandbox.py +5 -1
  4. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/url_policy.py +27 -1
  5. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_bootstrap.py +166 -25
  6. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_crawler_fetch.py +9 -1
  7. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_worker.py +511 -9
  8. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/studio_boundary.py +1 -1
  9. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/router.py +41 -1
  10. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/hosted_acquisition.py +29 -4
  11. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/remediation.py +1 -1
  12. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/render.py +1 -0
  13. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/.gitignore +0 -0
  14. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/README.md +0 -0
  15. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/scripts/hatch_build.py +0 -0
  16. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/__init__.py +0 -0
  17. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/__init__.py +0 -0
  18. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/http.py +0 -0
  19. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/parsing.py +0 -0
  20. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/ranges.py +0 -0
  21. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/result_download.py +0 -0
  22. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/acquisition/retention_admission.py +0 -0
  23. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/agent_runtime.py +0 -0
  24. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/assets/logo-ink.svg +0 -0
  25. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/__init__.py +0 -0
  26. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/pandas_backend.py +0 -0
  27. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/polars_backend.py +0 -0
  28. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/protocol.py +0 -0
  29. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/reference.py +0 -0
  30. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/registry.py +0 -0
  31. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/backends/restrictions.py +0 -0
  32. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/canonical.py +0 -0
  33. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/catalog_job.py +0 -0
  34. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/cli.py +0 -0
  35. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/contracts.py +0 -0
  36. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/coordinator.py +0 -0
  37. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/deploy.py +0 -0
  38. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/deploy_target.py +0 -0
  39. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/deployment_evidence.py +0 -0
  40. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/event_presentation.py +0 -0
  41. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/events.py +0 -0
  42. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/fast_delimited.py +0 -0
  43. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/fleet.py +0 -0
  44. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/formats.py +0 -0
  45. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/governors.py +0 -0
  46. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_crawler.py +0 -0
  47. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_crawler_container_smoke.py +0 -0
  48. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_crawler_job.py +0 -0
  49. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
  50. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_dataset.py +0 -0
  51. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_deploy.py +0 -0
  52. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_handoff.py +0 -0
  53. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_ingestion_contract.py +0 -0
  54. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_ingestion_job.py +0 -0
  55. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_ingestion_job_smoke.py +0 -0
  56. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_session_container_smoke.py +0 -0
  57. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_session_worker.py +0 -0
  58. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/hosted_session_worker_job_smoke.py +0 -0
  59. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ingestion/__init__.py +0 -0
  60. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ingestion/contracts.py +0 -0
  61. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ingestion/faults.py +0 -0
  62. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ingestion/gcs_store.py +0 -0
  63. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ingestion/spool.py +0 -0
  64. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ingestion/store.py +0 -0
  65. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/key_seam.py +0 -0
  66. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/linux_process_boundary.py +0 -0
  67. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_contracts.py +0 -0
  68. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/__init__.py +0 -0
  69. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/build_index.py +0 -0
  70. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/contracts.py +0 -0
  71. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/query_trace.py +0 -0
  72. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/retrieval.py +0 -0
  73. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/sealed.py +0 -0
  74. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/local_search/service.py +0 -0
  75. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/CONTRACT.md +0 -0
  76. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/__init__.py +0 -0
  77. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/chrome.py +0 -0
  78. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/code_body.py +0 -0
  79. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/document.py +0 -0
  80. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/frame.py +0 -0
  81. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/interactive.py +0 -0
  82. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/markdown_body.py +0 -0
  83. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/mr_components.py +0 -0
  84. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/outputs_data.py +0 -0
  85. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/outputs_rich.py +0 -0
  86. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/outputs_source.py +0 -0
  87. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/outputs_stage.py +0 -0
  88. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/outputs_text.py +0 -0
  89. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/parse.py +0 -0
  90. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/status.py +0 -0
  91. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/nbrender/tokens.py +0 -0
  92. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/notebook.py +0 -0
  93. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/offline.py +0 -0
  94. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/operation_registry.py +0 -0
  95. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/operator_setup.py +0 -0
  96. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/pipeline.py +0 -0
  97. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/plan_graph.py +0 -0
  98. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/__init__.py +0 -0
  99. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/contracts.py +0 -0
  100. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/engine.py +0 -0
  101. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/errors.py +0 -0
  102. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/gates.py +0 -0
  103. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/joins.py +0 -0
  104. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/profile.py +0 -0
  105. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/table.py +0 -0
  106. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/preparation/transforms.py +0 -0
  107. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/progress_events.py +0 -0
  108. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/__init__.py +0 -0
  109. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/containers.py +0 -0
  110. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/contracts.py +0 -0
  111. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/delimited.py +0 -0
  112. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/grib2/__init__.py +0 -0
  113. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/grib2/admission.py +0 -0
  114. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/grib2/decode.py +0 -0
  115. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/grib2/geometry.py +0 -0
  116. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/grib2/portable_math.py +0 -0
  117. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/json_tabular.py +0 -0
  118. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/parquet_tabular.py +0 -0
  119. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/registry.py +0 -0
  120. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/README.md +0 -0
  121. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  122. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +0 -0
  123. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  124. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +0 -0
  125. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  126. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +0 -0
  127. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  128. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +0 -0
  129. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  130. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +0 -0
  131. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  132. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +0 -0
  133. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  134. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +0 -0
  135. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +0 -0
  136. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +0 -0
  137. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +0 -0
  138. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +0 -0
  139. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +0 -0
  140. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +0 -0
  141. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +0 -0
  142. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +0 -0
  143. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +0 -0
  144. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +0 -0
  145. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/parquet.tabular/1.0.0/two_columns/expected.json +0 -0
  146. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/parquet.tabular/1.0.0/two_columns/prices.parquet +0 -0
  147. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  148. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +0 -0
  149. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +0 -0
  150. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  151. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +0 -0
  152. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  153. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +0 -0
  154. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +0 -0
  155. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  156. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +0 -0
  157. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  158. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +0 -0
  159. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  160. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +0 -0
  161. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  162. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/samples.py +0 -0
  163. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/spreadsheet.py +0 -0
  164. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/readers/tabular.py +0 -0
  165. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/recipe.py +0 -0
  166. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/repair/__init__.py +0 -0
  167. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/repair/adapters.py +0 -0
  168. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/repair/contracts.py +0 -0
  169. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/repair/coordinator.py +0 -0
  170. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/repair/errors.py +0 -0
  171. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/review.py +0 -0
  172. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/rowset.py +0 -0
  173. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/serving.py +0 -0
  174. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/serving_edge.py +0 -0
  175. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/serving_http.py +0 -0
  176. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/session_probes.py +0 -0
  177. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/signing.py +0 -0
  178. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/source_discovery.py +0 -0
  179. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/__init__.py +0 -0
  180. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/_adapter_steps.py +0 -0
  181. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/adapters.py +0 -0
  182. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/authenticated_https.py +0 -0
  183. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/authenticated_https_v2.py +0 -0
  184. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/cadence.py +0 -0
  185. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/cadence_emission.py +0 -0
  186. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/cadence_history.py +0 -0
  187. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/__init__.py +0 -0
  188. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/admission.py +0 -0
  189. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/authoring.py +0 -0
  190. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/authoring_policy.py +0 -0
  191. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/authoring_shards.py +0 -0
  192. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/bounded_io.py +0 -0
  193. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/channel.py +0 -0
  194. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/channel_client.py +0 -0
  195. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/contracts.py +0 -0
  196. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/coverage.py +0 -0
  197. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/delta.py +0 -0
  198. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/embedding.py +0 -0
  199. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/entry_v2.py +0 -0
  200. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/fill.py +0 -0
  201. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/fill_partitions.py +0 -0
  202. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/fill_staging.py +0 -0
  203. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/gating.py +0 -0
  204. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/generation_receipt.py +0 -0
  205. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/harvest/__init__.py +0 -0
  206. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/harvest/ckan.py +0 -0
  207. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +0 -0
  208. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/harvest/protocol.py +0 -0
  209. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/harvest/sdmx.py +0 -0
  210. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/harvest/stac.py +0 -0
  211. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/health.py +0 -0
  212. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/hosted_catalog.py +0 -0
  213. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/identity_history.py +0 -0
  214. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/neural.py +0 -0
  215. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/packed_catalog.py +0 -0
  216. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/packed_retrieval.py +0 -0
  217. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/packed_writer.py +0 -0
  218. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/query_trace.py +0 -0
  219. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/recommend.py +0 -0
  220. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/retrieval.py +0 -0
  221. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/retrieval_manifest.py +0 -0
  222. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/rights_decisions.py +0 -0
  223. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/sealed.py +0 -0
  224. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/search.py +0 -0
  225. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/streaming_delta.py +0 -0
  226. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/catalog/update.py +0 -0
  227. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/collections.py +0 -0
  228. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/contracts.py +0 -0
  229. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/deletion.py +0 -0
  230. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/fitness.py +0 -0
  231. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/governance.py +0 -0
  232. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/hosted.py +0 -0
  233. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/integration.py +0 -0
  234. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/range_reader.py +0 -0
  235. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/registry.py +0 -0
  236. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/rights_rule.py +0 -0
  237. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/source_cadence_vectors.v1.json +0 -0
  238. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/sports.py +0 -0
  239. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream.py +0 -0
  240. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_capture_session.py +0 -0
  241. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_connector.py +0 -0
  242. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_connectors/polymarket.clob-market.v1.json +0 -0
  243. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_recorder.py +0 -0
  244. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_registry.py +0 -0
  245. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_rotation.py +0 -0
  246. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_series.py +0 -0
  247. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/sources/stream_transport.py +0 -0
  248. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/__init__.py +0 -0
  249. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/acquire.py +0 -0
  250. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
  251. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/approvals.py +0 -0
  252. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/commands.py +0 -0
  253. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/download.py +0 -0
  254. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/narrative.py +0 -0
  255. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/parity.py +0 -0
  256. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/propose.py +0 -0
  257. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/research.py +0 -0
  258. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/runs.py +0 -0
  259. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/session.py +0 -0
  260. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/stream.py +0 -0
  261. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/transport.py +0 -0
  262. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
  263. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/thin/workers.py +0 -0
  264. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ucum/TABLE-PIN.json +0 -0
  265. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ucum/ucum-subset.v1.json +0 -0
  266. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/unit_flow.py +0 -0
  267. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/units.py +0 -0
  268. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/__init__.py +0 -0
  269. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/approve.py +0 -0
  270. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/author_yaml.py +0 -0
  271. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
  272. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
  273. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/approve.py +0 -0
  274. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
  275. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/author.py +0 -0
  276. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/catalog_author.py +0 -0
  277. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/catalog_fill.py +0 -0
  278. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/catalog_harvest.py +0 -0
  279. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/catalog_publish.py +0 -0
  280. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/catalog_search.py +0 -0
  281. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/catalog_update.py +0 -0
  282. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/deploy.py +0 -0
  283. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/deploy_dataset.py +0 -0
  284. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/deploy_plan.py +0 -0
  285. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/deploy_status.py +0 -0
  286. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/diff.py +0 -0
  287. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/index.py +0 -0
  288. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/inventory.py +0 -0
  289. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/list_builds.py +0 -0
  290. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
  291. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/peek.py +0 -0
  292. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/plan_check.py +0 -0
  293. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/preflight.py +0 -0
  294. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/record.py +0 -0
  295. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/review_setup.py +0 -0
  296. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/search.py +0 -0
  297. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/show.py +0 -0
  298. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
  299. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
  300. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
  301. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/credentials.py +0 -0
  302. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/diffing.py +0 -0
  303. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/headline.py +0 -0
  304. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/hosted_run_status.py +0 -0
  305. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/inventory.py +0 -0
  306. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/local_review.py +0 -0
  307. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/login.py +0 -0
  308. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
  309. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/peek.py +0 -0
  310. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
  311. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/plan_check.py +0 -0
  312. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/preflight.py +0 -0
  313. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/readers.py +0 -0
  314. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/ux/workload.py +0 -0
  315. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/viewer.py +0 -0
  316. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/__init__.py +0 -0
  317. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/authoring.py +0 -0
  318. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/contracts.py +0 -0
  319. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/materialize.py +0 -0
  320. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/observations.py +0 -0
  321. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/query.py +0 -0
  322. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/reducer.py +0 -0
  323. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/sdk.py +0 -0
  324. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/store.py +0 -0
  325. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/visual_run/transport.py +0 -0
  326. {mostlyright_data-0.9.6 → mostlyright_data-0.9.7}/src/mostlyright/data_harness/watch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mostlyright-data
3
- Version: 0.9.6
3
+ Version: 0.9.7
4
4
  Summary: Mostly Right CLI for reproducible, reviewed Parquet datasets
5
5
  Project-URL: Homepage, https://mostlyright.md/
6
6
  Project-URL: Documentation, https://mostlyright.md/docs/guides/cli/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mostlyright-data"
3
- version = "0.9.6"
3
+ version = "0.9.7"
4
4
  description = "Mostly Right CLI for reproducible, reviewed Parquet datasets"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13,<3.14"
@@ -59,6 +59,7 @@ from mostlyright.data_harness.acquisition.url_policy import (
59
59
  AcquisitionSecurityError,
60
60
  EgressPolicy,
61
61
  SystemResolver,
62
+ canonical_peer_order,
62
63
  validate_public_https_url,
63
64
  )
64
65
  from mostlyright.data_harness.canonical import canonical_sha256, sha256_bytes
@@ -3011,7 +3012,10 @@ def _parse_approved_endpoints(
3011
3012
  )
3012
3013
  normalized.append(address.compressed)
3013
3014
  key = (hostname, port)
3014
- if key in endpoints or tuple(sorted(normalized)) != addresses:
3015
+ # The SAME order `url_policy.canonical_peer_order` writes, read from that function rather
3016
+ # than restated: the parent decides connection order there, and a second sorting rule here
3017
+ # would refuse the very sets the parent now produces (#351).
3018
+ if key in endpoints or canonical_peer_order(normalized) != addresses:
3015
3019
  raise AcquisitionSecurityError(
3016
3020
  "SANDBOX_EGRESS",
3017
3021
  "approved endpoint set is duplicate or non-canonical",
@@ -6,6 +6,7 @@ import ipaddress
6
6
  import re
7
7
  import socket
8
8
  import unicodedata
9
+ from collections.abc import Iterable
9
10
  from dataclasses import dataclass
10
11
  from typing import Protocol
11
12
  from urllib.parse import SplitResult, urlsplit, urlunsplit
@@ -188,6 +189,31 @@ class ValidatedTarget:
188
189
  return urlunsplit(("", "", parsed.path or "/", parsed.query, ""))
189
190
 
190
191
 
192
+ def canonical_peer_order(addresses: Iterable[str]) -> tuple[str, ...]:
193
+ """The one order an approved peer set is ever written in: IPv4 first, then lexicographic.
194
+
195
+ ⚠ THIS IS A CONNECTION ORDER, NOT ONLY A CANONICAL FORM. A fetch spends a bounded request
196
+ budget (``max_requests``, four) walking this tuple, so whatever sorts first is what an
197
+ IPv4-only deployment -- every Cloud Run job -- actually dials. Plain lexicographic sorting put
198
+ ``2606:…`` ahead of ``3.x``/``34.x``/``52.x``, which is precisely the A-record shape every
199
+ Fastly and CloudFront host has, so an IPv6-unroutable worker burned its whole budget on AAAA
200
+ candidates and died ``REQUEST_LIMIT`` before reaching a routable address (#351).
201
+
202
+ Family first is therefore the rule, and it is a rule rather than a preference expressed in the
203
+ resolver: ordering is decided HERE, and ``sandbox._parse_approved_endpoints`` refuses an
204
+ endpoint set that is not in this exact order, so the two must move together. Within a family
205
+ the order stays lexicographic on the compressed text, which keeps the set canonical -- the
206
+ resolution digest is computed over it -- and keeps it independent of resolver answer order.
207
+ """
208
+
209
+ return tuple(
210
+ sorted(
211
+ dict.fromkeys(addresses),
212
+ key=lambda address: (0 if ipaddress.ip_address(address).version == 4 else 1, address),
213
+ )
214
+ )
215
+
216
+
191
217
  def approved_public_addresses(
192
218
  hostname: str,
193
219
  port: int,
@@ -233,7 +259,7 @@ def approved_public_addresses(
233
259
  ) from None
234
260
  _require_globally_routable(address)
235
261
  approved.append(address.compressed)
236
- approved_ips = tuple(sorted(dict.fromkeys(approved)))
262
+ approved_ips = canonical_peer_order(dict.fromkeys(approved))
237
263
  if not approved_ips:
238
264
  raise AcquisitionSecurityError("DNS_EMPTY", "target has no approved public addresses")
239
265
  if len(approved_ips) > MAX_DNS_ANSWERS:
@@ -22,7 +22,7 @@ from datetime import UTC, datetime
22
22
  from pathlib import Path
23
23
  from typing import Any, NoReturn, Protocol
24
24
  from urllib.error import HTTPError
25
- from urllib.parse import urlencode, urlsplit
25
+ from urllib.parse import urlencode, urlsplit, urlunsplit
26
26
  from urllib.request import (
27
27
  HTTPRedirectHandler,
28
28
  HTTPSHandler,
@@ -42,6 +42,7 @@ from mostlyright.data_harness.governors import (
42
42
  Reservation,
43
43
  )
44
44
  from mostlyright.data_harness.hosted_worker import (
45
+ CRAWLER_RESULTS_BUCKET_ENV,
45
46
  MAX_CAPABILITY_BYTES,
46
47
  MAX_DESCRIPTOR_BYTES,
47
48
  MAX_SIGNING_KEY_BYTES,
@@ -62,6 +63,7 @@ BOOTSTRAP_REQUEST_SCHEMA = "mostlyright-worker-bootstrap-request.v3"
62
63
  BOOTSTRAP_RESPONSE_SCHEMA = "mostlyright-worker-bootstrap-response.v3"
63
64
  GENERIC_PROBE_BOOTSTRAP_RESPONSE_SCHEMA = "mostlyright-worker-bootstrap-response.v5"
64
65
  BOOTSTRAP_EXCHANGE_PATH = "/_internal/worker-bootstrap:exchange"
66
+ BOOTSTRAP_FAILURE_PATH = "/_internal/worker-bootstrap:fail"
65
67
  GENERIC_PROBE_BOOTSTRAP_EXCHANGE_PATH = "/_internal/generic-api-probe-bootstrap:exchange"
66
68
  METADATA_IDENTITY_URL = (
67
69
  "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity"
@@ -69,6 +71,21 @@ METADATA_IDENTITY_URL = (
69
71
  MAX_IDENTITY_BYTES = 16 * 1024
70
72
  MAX_BOOTSTRAP_RESPONSE_BYTES = 768 * 1024
71
73
  BOOTSTRAP_TIMEOUT_SECONDS = 10.0
74
+ #: Exchange refusals worth waiting out. Everything else in 4xx is Studio's final answer for this
75
+ #: dispatch, so the attempt is dead and something has to say so before this process exits.
76
+ RETRYABLE_EXCHANGE_STATUSES = frozenset({408, 425, 429})
77
+ #: The closed set of reasons a bootstrap can report before it holds a job, one per refusal it can
78
+ #: be told. Keep it closed in lock-step with Studio's accepted set: a code Studio does not know is
79
+ #: refused, the report is lost, and the run waits for the stuck-run poller instead.
80
+ EXCHANGE_REFUSAL_CODES = {
81
+ 400: "BOOTSTRAP_EXCHANGE_REJECTED",
82
+ 401: "BOOTSTRAP_IDENTITY_REJECTED",
83
+ 403: "BOOTSTRAP_IDENTITY_REJECTED",
84
+ 404: "BOOTSTRAP_DISPATCH_UNCLAIMABLE",
85
+ 409: "BOOTSTRAP_DISPATCH_UNCLAIMABLE",
86
+ 410: "BOOTSTRAP_DISPATCH_UNCLAIMABLE",
87
+ 422: "BOOTSTRAP_EXCHANGE_REJECTED",
88
+ }
72
89
  WORKER_BIN_DIRECTORY = Path("/opt/worker/bin")
73
90
  _JWT = re.compile(r"^[A-Za-z0-9_-]{1,8192}\.[A-Za-z0-9_-]{1,8192}\.[A-Za-z0-9_-]{1,8192}$")
74
91
  _DISPATCH_QUERY = re.compile(
@@ -284,6 +301,14 @@ def _local_validation_policy_digests() -> dict[str, str]:
284
301
 
285
302
 
286
303
  def _require_local_policy_binding(binding: WorkerPolicyBinding) -> None:
304
+ """Fail a misdeployed image before it spends an exchange. Not the authority on a run.
305
+
306
+ The run's own authority is the sealed preflight, which ``_require_worker_policy_preflight``
307
+ holds against this same runtime. This one only catches an image whose deployment record and
308
+ whose code disagree, which no run can be sealed correctly against, and it catches it one
309
+ round trip earlier.
310
+ """
311
+
287
312
  local = _local_validation_policy_digests()
288
313
  if binding.validation_policy_digests != local:
289
314
  deployed = canonical_json_bytes(binding.validation_policy_digests).decode("utf-8")
@@ -434,6 +459,46 @@ def _metadata_identity(
434
459
  return bytearray(raw)
435
460
 
436
461
 
462
+ def _report_attempt_failure(
463
+ config: BootstrapConfig,
464
+ transport: BootstrapTransport,
465
+ identity: bytearray,
466
+ status: int,
467
+ ) -> bool:
468
+ """Tell Studio, best effort, that this attempt will never start. Return whether it landed.
469
+
470
+ A refused bootstrap is the one failure the attempt itself cannot report: the process holds no
471
+ job, no capability and no attempt id, so nothing downstream of it will ever call ``fail``. Left
472
+ silent, the run sits in "running" until a human notices. This is the cheapest way to close that
473
+ gap, not the guarantee -- Studio's stuck-run poller is what actually guarantees it, so a report
474
+ that cannot be delivered changes nothing about how this process exits.
475
+
476
+ It reuses the attempt's own identity token, sends only the allowlisted refusal code in
477
+ canonical bytes on the ``:fail`` sibling of the exchange route, and accepts only an empty 204.
478
+ Nothing a compromised Studio could answer here can cross back into the worker.
479
+ """
480
+
481
+ code = EXCHANGE_REFUSAL_CODES.get(status)
482
+ if code is None:
483
+ return False
484
+ parsed = urlsplit(config.exchange_url)
485
+ try:
486
+ reported, raw = transport.request(
487
+ "POST",
488
+ urlunsplit((parsed.scheme, parsed.netloc, BOOTSTRAP_FAILURE_PATH, parsed.query, "")),
489
+ {
490
+ "Accept": "application/json",
491
+ "Authorization": "Bearer " + identity.decode("ascii"),
492
+ "Content-Type": "application/json",
493
+ },
494
+ canonical_json_bytes({"failure_code": code}),
495
+ 0,
496
+ )
497
+ except Exception:
498
+ return False
499
+ return reported == 204 and not raw
500
+
501
+
437
502
  def _exchange(
438
503
  config: BootstrapConfig,
439
504
  transport: BootstrapTransport,
@@ -457,6 +522,14 @@ def _exchange(
457
522
  MAX_BOOTSTRAP_RESPONSE_BYTES,
458
523
  )
459
524
  if status != 200:
525
+ # The generic API probe is excluded on purpose: it has no run and no attempt behind it, so
526
+ # there is no state anywhere that a report would move off "running".
527
+ if (
528
+ config.role != "generic_api"
529
+ and 400 <= status < 500
530
+ and status not in RETRYABLE_EXCHANGE_STATUSES
531
+ ):
532
+ _report_attempt_failure(config, transport, identity, status)
460
533
  raise HostedWorkerError(
461
534
  "BOOTSTRAP_EXCHANGE_FAILED",
462
535
  f"Studio bootstrap exchange returned HTTP {status}",
@@ -475,9 +548,19 @@ def _require_worker_policy_preflight(
475
548
  job_wire: Any,
476
549
  job: Any,
477
550
  response_preflight: Any,
478
- binding: WorkerPolicyBinding,
479
551
  ) -> None:
480
- """Bind the deployed selection, Studio response, and parsed recipe mode before execution."""
552
+ """Bind the Studio response, the sealed preflight and the parsed recipe before execution.
553
+
554
+ The sealed preflight is the run's single fleet authority. The image digests, selection id and
555
+ release fence it names are deliberately NOT re-compared against a second copy carried in this
556
+ worker's environment: that mirror could only ever say "the fleet moved on since this run was
557
+ sealed", which is not a reason to refuse a run whose tooling was chosen for it. Nine hosted
558
+ builds died as WORKER_POLICY_BINDING_MISMATCH saying exactly that.
559
+
560
+ What survives is everything the traveler can be held to without a second copy: both preflight
561
+ copies agree, the field set is exact, it names this job's scope and this recipe's mode, its
562
+ policy set is internally consistent, and that policy set is the one this runtime implements.
563
+ """
481
564
 
482
565
  if not isinstance(job_wire, dict):
483
566
  raise HostedWorkerError(
@@ -524,6 +607,7 @@ def _require_worker_policy_preflight(
524
607
  "table_recipe_id": job.table_recipe_id,
525
608
  "recipe_version": job.recipe_version,
526
609
  "recipe_digest": job.documents.recipe_digest,
610
+ "policy_mode": mode,
527
611
  }
528
612
  if any(job_preflight.get(name) != value for name, value in expected_scope.items()) or (
529
613
  "table_id" in job_preflight and job_preflight["table_id"] != str(job.table_id)
@@ -532,21 +616,41 @@ def _require_worker_policy_preflight(
532
616
  "WORKER_POLICY_BINDING_MISMATCH",
533
617
  "worker policy preflight scope differs from the bootstrap job",
534
618
  )
535
- static = {
536
- "builder_image_digest": binding.producer_image_digest,
537
- "verifier_image_digest": binding.verifier_image_digest,
538
- "validation_policy_digests": binding.validation_policy_digests,
539
- "policy_set_digest": binding.policy_set_digest,
540
- "selection_fence": binding.release_revision_coordinate,
541
- "selection_id": binding.selection_id,
542
- "policy_mode": mode,
543
- "validation_policy_digest": binding.validation_policy_digests[mode],
544
- }
545
- mismatched = [name for name, value in static.items() if job_preflight.get(name) != value]
546
- if mismatched:
619
+ digests = job_preflight["validation_policy_digests"]
620
+ # Shape first, and before anything renders it. The refusals below quote the sealed set back as
621
+ # canonical JSON, and canonical JSON is a narrower dialect than the one the response was parsed
622
+ # in -- a non-integer number inside this field would raise out of the renderer, past the coded
623
+ # refusal it was rendering, and reach the operator as a traceback with no code on it.
624
+ if (
625
+ not isinstance(digests, dict)
626
+ or set(digests) != {"table", "graph"}
627
+ or any(not isinstance(digest, str) for digest in digests.values())
628
+ ):
629
+ raise HostedWorkerError(
630
+ "WORKER_POLICY_BINDING_MISMATCH",
631
+ "worker policy preflight validation policy set is not one table and graph digest pair",
632
+ )
633
+ # The runtime is the authority on which validation policy this image implements, so the sealed
634
+ # set is checked against the code rather than against a deployment variable claiming to
635
+ # describe the code. A run sealed under a policy this image does not implement would attest to
636
+ # a check it never ran.
637
+ local = _local_validation_policy_digests()
638
+ if digests != local:
639
+ sealed = canonical_json_bytes(digests).decode("utf-8")
640
+ computed = canonical_json_bytes(local).decode("utf-8")
641
+ raise HostedWorkerError(
642
+ "WORKER_POLICY_RUNTIME_MISMATCH",
643
+ f"sealed validation policy set {sealed} differs from this worker runtime {computed}; "
644
+ "launch this run on the worker image its preflight sealed",
645
+ )
646
+ if (
647
+ job_preflight["validation_policy_digest"] != digests[mode]
648
+ or job_preflight["policy_set_digest"]
649
+ != "sha256:" + hashlib.sha256(canonical_json_bytes(digests)).hexdigest()
650
+ ):
547
651
  raise HostedWorkerError(
548
652
  "WORKER_POLICY_BINDING_MISMATCH",
549
- "worker policy preflight differs from the deployed selection: " + ",".join(mismatched),
653
+ "worker policy preflight does not carry one self-consistent policy set",
550
654
  )
551
655
 
552
656
 
@@ -611,7 +715,6 @@ def _validate_bootstrap_response(
611
715
  job_wire=job_wire,
612
716
  job=parsed_job,
613
717
  response_preflight=value["worker_policy_preflight"],
614
- binding=worker_policy_binding,
615
718
  )
616
719
  return {"job": job_raw, "capability": capability_raw}, parsed_job
617
720
 
@@ -625,7 +728,6 @@ def _validate_bootstrap_response(
625
728
  job_wire=job_wire,
626
729
  job=parsed_job,
627
730
  response_preflight=value["worker_policy_preflight"],
628
- binding=worker_policy_binding,
629
731
  )
630
732
  signing_key = value["signing_key"]
631
733
  parse_signing_key(signing_key)
@@ -977,6 +1079,19 @@ def launch(
977
1079
  "PYTHONDONTWRITEBYTECODE": "1",
978
1080
  "PYTHONUNBUFFERED": "1",
979
1081
  }
1082
+ # The hygiene exec strips the inherited environment so no credential can leak into
1083
+ # the worker -- and it must keep doing so. But the crawler-results bucket is a public
1084
+ # deployment coordinate, not a secret: it names which GCS bucket a result download's
1085
+ # storage authority may claim, the worker's download check refuses every GCS-served
1086
+ # crawl without it, and the deploy pins it on the job for exactly that check. It is
1087
+ # the ONE inherited name that crosses; anything else stays stripped.
1088
+ # It is the FALLBACK, not the source: a job that carries `results_bucket` travels with
1089
+ # its own pin and the worker prefers that one. This pass-through covers dispatch
1090
+ # payloads minted before the traveler did; it retires once every dispatch mints
1091
+ # the field.
1092
+ results_bucket = os.environ.get(CRAWLER_RESULTS_BUCKET_ENV)
1093
+ if results_bucket:
1094
+ environment[CRAWLER_RESULTS_BUCKET_ENV] = results_bucket
980
1095
  allowed = {payload.descriptor for payload in descriptors.values()}
981
1096
  changed = _restrict_inherited_descriptors(allowed)
982
1097
  try:
@@ -997,21 +1112,47 @@ def launch(
997
1112
 
998
1113
 
999
1114
  def main(argv: Sequence[str] | None = None) -> int:
1115
+ """Parse one Cloud Run Job dispatch and launch the bootstrap it names.
1116
+
1117
+ Two dispatch spellings reach this executable, because two Studio enqueuers write them and
1118
+ each names the coordinate it carries. A Builder or verifier task carries
1119
+ ``--exchange-url ...?dispatch_id=<uuid> --role {producer,verifier}``. A standalone generic
1120
+ API probe carries ``--generic-api-probe-bootstrap-url ...?probe_id=<uuid>`` and no ``--role``
1121
+ at all: the flag names the probe exchange, so the role is not a second thing to state and
1122
+ Studio's ``GoogleCloudTasksGenericApiProbeEnqueuer`` does not state it.
1123
+
1124
+ Accepting only the first spelling is what made the hosted keyed-source probe lane
1125
+ unreachable: argparse rejected the deployed probe task's argv outright (``unrecognized
1126
+ arguments`` plus a missing ``--role``) and the Job exited 2 before a single line of bootstrap
1127
+ code ran. ``tests/test_hosted_bootstrap.py`` holds this parser against the argv Studio's own
1128
+ ``dispatch.py`` emits, so the two cannot drift apart again silently.
1129
+ """
1130
+
1000
1131
  parser = argparse.ArgumentParser()
1001
- parser.add_argument("--exchange-url", required=True)
1132
+ source = parser.add_mutually_exclusive_group(required=True)
1133
+ source.add_argument("--exchange-url")
1134
+ source.add_argument("--generic-api-probe-bootstrap-url")
1002
1135
  parser.add_argument("--audience", required=True)
1003
- parser.add_argument("--role", required=True, choices=("producer", "verifier", "generic_api"))
1136
+ parser.add_argument("--role", choices=("producer", "verifier", "generic_api"))
1004
1137
  arguments = parser.parse_args(argv)
1138
+ if arguments.generic_api_probe_bootstrap_url is not None:
1139
+ if arguments.role not in {None, "generic_api"}:
1140
+ parser.error("--generic-api-probe-bootstrap-url admits only --role generic_api")
1141
+ exchange_url = arguments.generic_api_probe_bootstrap_url
1142
+ role = "generic_api"
1143
+ else:
1144
+ if arguments.role is None:
1145
+ parser.error("--exchange-url requires --role")
1146
+ exchange_url = arguments.exchange_url
1147
+ role = arguments.role
1005
1148
  try:
1006
1149
  launch(
1007
1150
  BootstrapConfig(
1008
- exchange_url=arguments.exchange_url,
1151
+ exchange_url=exchange_url,
1009
1152
  audience=arguments.audience,
1010
- role=arguments.role,
1153
+ role=role,
1011
1154
  worker_policy_binding=(
1012
- None
1013
- if arguments.role == "generic_api"
1014
- else _binding_from_environment(os.environ)
1155
+ None if role == "generic_api" else _binding_from_environment(os.environ)
1015
1156
  ),
1016
1157
  )
1017
1158
  )
@@ -139,7 +139,15 @@ def _public_https_retriever(
139
139
  max_concurrency=2,
140
140
  min_interval_seconds=1.0,
141
141
  allowed_media_types=_public_expected_media_types(request.query),
142
- user_agent="MostlyRightDataHarness-PublicHttps/1.0",
142
+ # The contact URL is load-bearing, not decoration: Akamai-fronted
143
+ # providers (stlouisfed.org, measured 2026-08-27 in harness#430)
144
+ # silently tarpit a bare unknown product token - RST on h2, a
145
+ # 15s+ hang on http/1.1 that surfaces as the acquisition timeout -
146
+ # while the same token WITH the crawler-convention "(+URL)"
147
+ # segment answers in half a second from the same egress IP. An
148
+ # honest identity that names where to reach us is also simply the
149
+ # right thing for a public crawler to send.
150
+ user_agent="MostlyRightDataHarness-PublicHttps/1.0 (+https://mostlyright.md/crawler)",
143
151
  ),
144
152
  limiter=_PUBLIC_HTTPS_LIMITER,
145
153
  )