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,995 @@
1
+ """Strict v2 envelope for independently sealed catalog retrieval representations.
2
+
3
+ The ``harness-source-catalog-index.v1`` artifact remains the representation format.
4
+ This module adds an outer manifest; it does not reinterpret the v1 backend coordinate or its
5
+ ``query_safe`` flag. A lexical representation is always present. A neural representation is
6
+ optional and, when present, carries the exact explicit local encoder and model-artifact closure
7
+ that the v2 query path is allowed to resolve.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import re
14
+ import stat
15
+ import struct
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ from mostlyright.data_harness.canonical import (
21
+ CanonicalJSONError,
22
+ canonical_json_bytes,
23
+ parse_canonical_json,
24
+ sha256_bytes,
25
+ )
26
+ from mostlyright.data_harness.sources.catalog.contracts import (
27
+ CATALOG_INDEX_CONTRACT_VERSION,
28
+ EMBEDDING_LAYERS,
29
+ CatalogEntry,
30
+ catalog_entry_from_dict,
31
+ )
32
+ from mostlyright.data_harness.sources.catalog.embedding import (
33
+ BackendDescriptor,
34
+ EmbeddingBackend,
35
+ LexicalHashingBackend,
36
+ PinnedLocalBackendDescriptor,
37
+ )
38
+ from mostlyright.data_harness.sources.contracts import SourceContractError
39
+
40
+ RETRIEVAL_MANIFEST_SCHEMA = "harness-source-catalog-retrieval.v2"
41
+ LEXICAL_REPRESENTATION = "lexical"
42
+ NEURAL_REPRESENTATION = "neural"
43
+ LEXICAL_FALLBACK_REASONS = frozenset(
44
+ {
45
+ "NEURAL_NOT_REQUESTED",
46
+ "NEURAL_RUNTIME_UNAVAILABLE",
47
+ "NEURAL_TUPLE_UNAVAILABLE",
48
+ "NEURAL_MODEL_UNAVAILABLE",
49
+ }
50
+ )
51
+ MAX_REPRESENTATION_MEMBERS = 40_002
52
+ MAX_REPRESENTATION_MEMBER_BYTES = 128 * 1024 * 1024
53
+ RETRIEVAL_MANIFEST_FILENAME = "retrieval.json"
54
+ MAX_RETRIEVAL_MANIFEST_BYTES = 4 * 1024 * 1024
55
+ REPRESENTATION_DESCRIPTOR_FILENAME = "descriptor.json"
56
+ REPRESENTATION_DESCRIPTOR_SCHEMA = "harness-source-catalog-representation-descriptor.v1"
57
+ MAX_REPRESENTATION_DESCRIPTOR_BYTES = 1_024
58
+
59
+ _SHA256 = re.compile(r"^[0-9a-f]{64}$")
60
+ _IDENTIFIER = re.compile(r"^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$")
61
+ _SEMVER = re.compile(r"^[1-9][0-9]*\.[0-9]+\.[0-9]+$")
62
+
63
+
64
+ class RetrievalManifestError(SourceContractError):
65
+ """A stable refusal for an invalid or ambiguous v2 retrieval manifest."""
66
+
67
+
68
+ @dataclass(frozen=True)
69
+ class RepresentationMember:
70
+ """One exact regular-file member in a representation closure."""
71
+
72
+ path: str
73
+ size: int
74
+ sha256: str
75
+
76
+ def __post_init__(self) -> None:
77
+ if (
78
+ not isinstance(self.path, str)
79
+ or not self.path
80
+ or self.path.startswith("/")
81
+ or "\\" in self.path
82
+ or any(part in {"", ".", ".."} for part in self.path.split("/"))
83
+ ):
84
+ raise RetrievalManifestError(
85
+ "RETRIEVAL_MANIFEST_MEMBER",
86
+ "retrieval.representations.members.path",
87
+ "must be a canonical relative POSIX path",
88
+ )
89
+ if type(self.size) is not int or not 0 <= self.size <= MAX_REPRESENTATION_MEMBER_BYTES:
90
+ raise RetrievalManifestError(
91
+ "RETRIEVAL_MANIFEST_MEMBER",
92
+ "retrieval.representations.members.size",
93
+ f"must be an integer in [0, {MAX_REPRESENTATION_MEMBER_BYTES}]",
94
+ )
95
+ _require_digest(
96
+ self.sha256,
97
+ "retrieval.representations.members.sha256",
98
+ "RETRIEVAL_MANIFEST_MEMBER",
99
+ )
100
+
101
+ def to_dict(self) -> dict[str, Any]:
102
+ return {"path": self.path, "sha256": self.sha256, "size": self.size}
103
+
104
+
105
+ @dataclass(frozen=True)
106
+ class RetrievalRepresentation:
107
+ """The exact schema, backend, encoder, and member closure for one representation."""
108
+
109
+ name: str
110
+ schema_version: str
111
+ backend_id: str
112
+ backend_version: str
113
+ dimensions: int
114
+ quantization: str
115
+ query_safe: bool
116
+ backend_coordinate: str
117
+ query_encoder: str
118
+ model_pin_sha256: str | None
119
+ model_artifacts: tuple[tuple[str, str], ...]
120
+ members: tuple[RepresentationMember, ...]
121
+ artifact_sha256: str
122
+
123
+ def __post_init__(self) -> None:
124
+ if self.name not in {LEXICAL_REPRESENTATION, NEURAL_REPRESENTATION}:
125
+ raise RetrievalManifestError(
126
+ "RETRIEVAL_MANIFEST_REPRESENTATIONS",
127
+ "retrieval.representations.name",
128
+ "must be lexical or neural",
129
+ )
130
+ if self.schema_version != CATALOG_INDEX_CONTRACT_VERSION:
131
+ raise RetrievalManifestError(
132
+ "RETRIEVAL_MANIFEST_SCHEMA",
133
+ f"retrieval.representations.{self.name}.schema_version",
134
+ f"must equal {CATALOG_INDEX_CONTRACT_VERSION!r}",
135
+ )
136
+ if not isinstance(self.backend_id, str) or _IDENTIFIER.fullmatch(self.backend_id) is None:
137
+ self._backend_error("backend_id is invalid")
138
+ if (
139
+ not isinstance(self.backend_version, str)
140
+ or _SEMVER.fullmatch(self.backend_version) is None
141
+ ):
142
+ self._backend_error("backend_version is invalid")
143
+ if type(self.dimensions) is not int or not 1 <= self.dimensions <= 4_096:
144
+ self._backend_error("dimensions are invalid")
145
+ if not isinstance(self.quantization, str) or not self.quantization:
146
+ self._backend_error("quantization is invalid")
147
+ if type(self.query_safe) is not bool:
148
+ self._backend_error("query_safe must be a boolean")
149
+ base_coordinate = f"{self.backend_id}@{self.backend_version}"
150
+ if self.name == LEXICAL_REPRESENTATION:
151
+ lexical = LexicalHashingBackend().descriptor
152
+ if (
153
+ not self.query_safe
154
+ or self.backend_coordinate != base_coordinate
155
+ or (
156
+ self.backend_id,
157
+ self.backend_version,
158
+ self.dimensions,
159
+ self.quantization,
160
+ self.query_safe,
161
+ )
162
+ != (
163
+ lexical.backend_id,
164
+ lexical.backend_version,
165
+ lexical.dimensions,
166
+ lexical.quantization,
167
+ lexical.query_safe,
168
+ )
169
+ ):
170
+ self._backend_error("lexical must retain its exact query-safe v1 coordinate")
171
+ if (
172
+ self.query_encoder != base_coordinate
173
+ or self.model_pin_sha256 is not None
174
+ or self.model_artifacts
175
+ ):
176
+ raise RetrievalManifestError(
177
+ "RETRIEVAL_MANIFEST_LEXICAL",
178
+ "retrieval.representations.lexical",
179
+ "lexical must use its v1 backend and must not carry model artifacts",
180
+ )
181
+ else:
182
+ if self.query_safe or not self.backend_coordinate.startswith(f"{base_coordinate}#"):
183
+ self._backend_error(
184
+ "neural must retain query_safe=false and use an explicit v2 coordinate"
185
+ )
186
+ if self.query_encoder != self.backend_coordinate:
187
+ raise RetrievalManifestError(
188
+ "RETRIEVAL_MANIFEST_NEURAL",
189
+ "retrieval.representations.neural.query_encoder",
190
+ "must name the exact governed local encoder coordinate",
191
+ )
192
+ _require_digest(
193
+ self.model_pin_sha256,
194
+ "retrieval.representations.neural.model_pin_sha256",
195
+ "RETRIEVAL_MANIFEST_NEURAL",
196
+ )
197
+ _validate_model_artifacts(self.model_artifacts)
198
+
199
+ if (
200
+ not isinstance(self.members, tuple)
201
+ or not self.members
202
+ or len(self.members) > MAX_REPRESENTATION_MEMBERS
203
+ or any(type(member) is not RepresentationMember for member in self.members)
204
+ ):
205
+ raise RetrievalManifestError(
206
+ "RETRIEVAL_MANIFEST_MEMBER",
207
+ f"retrieval.representations.{self.name}.members",
208
+ "must be a non-empty bounded tuple of strict members",
209
+ )
210
+ paths = tuple(member.path for member in self.members)
211
+ if paths != tuple(sorted(paths)) or len(set(paths)) != len(paths):
212
+ raise RetrievalManifestError(
213
+ "RETRIEVAL_MANIFEST_MEMBER",
214
+ f"retrieval.representations.{self.name}.members",
215
+ "must be path-sorted without duplicates",
216
+ )
217
+ prefix = f"{self.name}/"
218
+ catalog_path = next((path for path in paths if path.endswith("/catalog.json")), "")
219
+ expected_descriptor_path = (
220
+ f"{catalog_path[: -len('catalog.json')]}{REPRESENTATION_DESCRIPTOR_FILENAME}"
221
+ )
222
+ if (
223
+ any(not path.startswith(prefix) for path in paths)
224
+ or sum(path.endswith("/catalog.json") for path in paths) != 1
225
+ or sum(path.endswith(f"/{REPRESENTATION_DESCRIPTOR_FILENAME}") for path in paths) != 1
226
+ or expected_descriptor_path not in paths
227
+ ):
228
+ raise RetrievalManifestError(
229
+ "RETRIEVAL_MANIFEST_MEMBER",
230
+ f"retrieval.representations.{self.name}.members",
231
+ "members may bind only their representation and must include catalog.json and "
232
+ "descriptor.json once",
233
+ )
234
+ expected_artifact = sha256_bytes(
235
+ canonical_json_bytes([member.to_dict() for member in self.members])
236
+ )
237
+ if self.artifact_sha256 != expected_artifact:
238
+ raise RetrievalManifestError(
239
+ "RETRIEVAL_MANIFEST_ARTIFACT",
240
+ f"retrieval.representations.{self.name}.artifact_sha256",
241
+ "does not bind the exact sorted member closure",
242
+ )
243
+
244
+ @classmethod
245
+ def create(
246
+ cls,
247
+ *,
248
+ name: str,
249
+ descriptor: BackendDescriptor,
250
+ members: tuple[RepresentationMember, ...],
251
+ ) -> RetrievalRepresentation:
252
+ if not isinstance(descriptor, BackendDescriptor):
253
+ raise RetrievalManifestError(
254
+ "RETRIEVAL_MANIFEST_BACKEND",
255
+ f"retrieval.representations.{name}.backend",
256
+ "must use a strict backend descriptor",
257
+ )
258
+ ordered = tuple(sorted(members, key=lambda member: member.path))
259
+ model_artifacts: tuple[tuple[str, str], ...] = ()
260
+ model_pin_sha256: str | None = None
261
+ coordinate = descriptor.coordinate
262
+ if isinstance(descriptor, PinnedLocalBackendDescriptor):
263
+ model_pin_sha256 = descriptor.pin_sha256
264
+ model_artifacts = descriptor.artifact_sha256s
265
+ return cls(
266
+ name=name,
267
+ schema_version=CATALOG_INDEX_CONTRACT_VERSION,
268
+ backend_id=descriptor.backend_id,
269
+ backend_version=descriptor.backend_version,
270
+ dimensions=descriptor.dimensions,
271
+ quantization=descriptor.quantization,
272
+ query_safe=descriptor.query_safe,
273
+ backend_coordinate=coordinate,
274
+ query_encoder=coordinate,
275
+ model_pin_sha256=model_pin_sha256,
276
+ model_artifacts=model_artifacts,
277
+ members=ordered,
278
+ artifact_sha256=sha256_bytes(
279
+ canonical_json_bytes([member.to_dict() for member in ordered])
280
+ ),
281
+ )
282
+
283
+ def _backend_error(self, detail: str) -> None:
284
+ raise RetrievalManifestError(
285
+ "RETRIEVAL_MANIFEST_BACKEND",
286
+ f"retrieval.representations.{self.name}.backend",
287
+ detail,
288
+ )
289
+
290
+ def to_dict(self) -> dict[str, Any]:
291
+ return {
292
+ "artifact_sha256": self.artifact_sha256,
293
+ "backend": {
294
+ "backend_coordinate": self.backend_coordinate,
295
+ "backend_id": self.backend_id,
296
+ "backend_version": self.backend_version,
297
+ "dimensions": self.dimensions,
298
+ "quantization": self.quantization,
299
+ "query_safe": self.query_safe,
300
+ },
301
+ "members": [member.to_dict() for member in self.members],
302
+ "model_pin_sha256": self.model_pin_sha256,
303
+ "model_artifacts": [
304
+ {"path": path, "sha256": digest} for path, digest in self.model_artifacts
305
+ ],
306
+ "name": self.name,
307
+ "query_encoder": self.query_encoder,
308
+ "schema_version": self.schema_version,
309
+ }
310
+
311
+
312
+ @dataclass(frozen=True)
313
+ class RetrievalManifestV2:
314
+ """One lexical representation and one optional neural representation over shared facts."""
315
+
316
+ shared_facts_sha256: str
317
+ representations: tuple[RetrievalRepresentation, ...]
318
+ fallback_reason: str | None = None
319
+ schema_version: str = RETRIEVAL_MANIFEST_SCHEMA
320
+
321
+ def __post_init__(self) -> None:
322
+ if self.schema_version != RETRIEVAL_MANIFEST_SCHEMA:
323
+ raise RetrievalManifestError(
324
+ "RETRIEVAL_MANIFEST_VERSION",
325
+ "retrieval.schema_version",
326
+ f"must equal {RETRIEVAL_MANIFEST_SCHEMA!r}",
327
+ )
328
+ _require_digest(
329
+ self.shared_facts_sha256,
330
+ "retrieval.shared_facts_sha256",
331
+ "RETRIEVAL_MANIFEST_FACTS",
332
+ )
333
+ if (
334
+ not isinstance(self.representations, tuple)
335
+ or any(type(item) is not RetrievalRepresentation for item in self.representations)
336
+ or tuple(item.name for item in self.representations)
337
+ not in {
338
+ (LEXICAL_REPRESENTATION,),
339
+ (LEXICAL_REPRESENTATION, NEURAL_REPRESENTATION),
340
+ }
341
+ ):
342
+ raise RetrievalManifestError(
343
+ "RETRIEVAL_MANIFEST_REPRESENTATIONS",
344
+ "retrieval.representations",
345
+ "must contain lexical once followed by at most one neural representation",
346
+ )
347
+ if self.neural is None:
348
+ if self.fallback_reason not in LEXICAL_FALLBACK_REASONS:
349
+ raise RetrievalManifestError(
350
+ "RETRIEVAL_MANIFEST_FALLBACK",
351
+ "retrieval.fallback_reason",
352
+ "lexical-only v2 requires a typed neural fallback reason",
353
+ )
354
+ elif self.fallback_reason is not None:
355
+ raise RetrievalManifestError(
356
+ "RETRIEVAL_MANIFEST_FALLBACK",
357
+ "retrieval.fallback_reason",
358
+ "a hybrid manifest cannot also claim lexical-only fallback",
359
+ )
360
+
361
+ @property
362
+ def lexical(self) -> RetrievalRepresentation:
363
+ return self.representations[0]
364
+
365
+ @property
366
+ def neural(self) -> RetrievalRepresentation | None:
367
+ return self.representations[1] if len(self.representations) == 2 else None
368
+
369
+ @property
370
+ def canonical_bytes(self) -> bytes:
371
+ return canonical_json_bytes(self.to_dict())
372
+
373
+ @property
374
+ def digest(self) -> str:
375
+ return sha256_bytes(self.canonical_bytes)
376
+
377
+ def to_dict(self) -> dict[str, Any]:
378
+ return {
379
+ "fallback_reason": self.fallback_reason,
380
+ "representations": [item.to_dict() for item in self.representations],
381
+ "schema_version": self.schema_version,
382
+ "shared_facts_sha256": self.shared_facts_sha256,
383
+ }
384
+
385
+ @classmethod
386
+ def from_bytes(cls, raw: bytes) -> RetrievalManifestV2:
387
+ try:
388
+ payload = parse_canonical_json(raw)
389
+ except (CanonicalJSONError, TypeError, ValueError) as error:
390
+ raise RetrievalManifestError(
391
+ "RETRIEVAL_MANIFEST_CANONICAL",
392
+ "retrieval",
393
+ "manifest bytes must be canonical JSON",
394
+ ) from error
395
+ if not isinstance(payload, dict) or set(payload) != {
396
+ "fallback_reason",
397
+ "representations",
398
+ "schema_version",
399
+ "shared_facts_sha256",
400
+ }:
401
+ raise RetrievalManifestError(
402
+ "RETRIEVAL_MANIFEST_SHAPE",
403
+ "retrieval",
404
+ "manifest must contain the exact v2 field set",
405
+ )
406
+ raw_representations = payload["representations"]
407
+ if not isinstance(raw_representations, list):
408
+ raise RetrievalManifestError(
409
+ "RETRIEVAL_MANIFEST_REPRESENTATIONS",
410
+ "retrieval.representations",
411
+ "must be an array",
412
+ )
413
+ representations = tuple(
414
+ _representation_from_dict(item, index=index)
415
+ for index, item in enumerate(raw_representations)
416
+ )
417
+ try:
418
+ return cls(
419
+ schema_version=payload["schema_version"],
420
+ shared_facts_sha256=payload["shared_facts_sha256"],
421
+ fallback_reason=payload["fallback_reason"],
422
+ representations=representations,
423
+ )
424
+ except RetrievalManifestError:
425
+ raise
426
+ except (TypeError, ValueError) as error:
427
+ raise RetrievalManifestError(
428
+ "RETRIEVAL_MANIFEST_SHAPE", "retrieval", "manifest fields are invalid"
429
+ ) from error
430
+
431
+
432
+ def _representation_from_dict(value: Any, *, index: int) -> RetrievalRepresentation:
433
+ path = f"retrieval.representations[{index}]"
434
+ exact = {
435
+ "artifact_sha256",
436
+ "backend",
437
+ "members",
438
+ "model_artifacts",
439
+ "model_pin_sha256",
440
+ "name",
441
+ "query_encoder",
442
+ "schema_version",
443
+ }
444
+ if not isinstance(value, dict) or set(value) != exact:
445
+ raise RetrievalManifestError(
446
+ "RETRIEVAL_MANIFEST_REPRESENTATIONS", path, "representation fields differ"
447
+ )
448
+ backend = value["backend"]
449
+ if not isinstance(backend, dict) or set(backend) != {
450
+ "backend_coordinate",
451
+ "backend_id",
452
+ "backend_version",
453
+ "dimensions",
454
+ "quantization",
455
+ "query_safe",
456
+ }:
457
+ raise RetrievalManifestError(
458
+ "RETRIEVAL_MANIFEST_BACKEND", f"{path}.backend", "backend fields differ"
459
+ )
460
+ raw_members = value["members"]
461
+ if not isinstance(raw_members, list):
462
+ raise RetrievalManifestError(
463
+ "RETRIEVAL_MANIFEST_MEMBER", f"{path}.members", "must be an array"
464
+ )
465
+ members = tuple(_member_from_dict(item, path=f"{path}.members") for item in raw_members)
466
+ raw_artifacts = value["model_artifacts"]
467
+ if not isinstance(raw_artifacts, list):
468
+ raise RetrievalManifestError(
469
+ "RETRIEVAL_MANIFEST_NEURAL", f"{path}.model_artifacts", "must be an array"
470
+ )
471
+ artifacts: list[tuple[str, str]] = []
472
+ for item in raw_artifacts:
473
+ if not isinstance(item, dict) or set(item) != {"path", "sha256"}:
474
+ raise RetrievalManifestError(
475
+ "RETRIEVAL_MANIFEST_NEURAL",
476
+ f"{path}.model_artifacts",
477
+ "artifact fields differ",
478
+ )
479
+ artifacts.append((item["path"], item["sha256"]))
480
+ return RetrievalRepresentation(
481
+ name=value["name"],
482
+ schema_version=value["schema_version"],
483
+ backend_id=backend["backend_id"],
484
+ backend_version=backend["backend_version"],
485
+ dimensions=backend["dimensions"],
486
+ quantization=backend["quantization"],
487
+ query_safe=backend["query_safe"],
488
+ backend_coordinate=backend["backend_coordinate"],
489
+ query_encoder=value["query_encoder"],
490
+ model_pin_sha256=value["model_pin_sha256"],
491
+ model_artifacts=tuple(artifacts),
492
+ members=members,
493
+ artifact_sha256=value["artifact_sha256"],
494
+ )
495
+
496
+
497
+ def _member_from_dict(value: Any, *, path: str) -> RepresentationMember:
498
+ if not isinstance(value, dict) or set(value) != {"path", "sha256", "size"}:
499
+ raise RetrievalManifestError("RETRIEVAL_MANIFEST_MEMBER", path, "member fields differ")
500
+ return RepresentationMember(path=value["path"], size=value["size"], sha256=value["sha256"])
501
+
502
+
503
+ def _validate_model_artifacts(artifacts: tuple[tuple[str, str], ...]) -> None:
504
+ if not artifacts or tuple(sorted(artifacts)) != artifacts:
505
+ raise RetrievalManifestError(
506
+ "RETRIEVAL_MANIFEST_NEURAL",
507
+ "retrieval.representations.neural.model_artifacts",
508
+ "must be a non-empty path-sorted artifact closure",
509
+ )
510
+ for path, digest in artifacts:
511
+ if (
512
+ not isinstance(path, str)
513
+ or not path
514
+ or "/" in path
515
+ or "\\" in path
516
+ or _SHA256.fullmatch(digest) is None
517
+ ):
518
+ raise RetrievalManifestError(
519
+ "RETRIEVAL_MANIFEST_NEURAL",
520
+ "retrieval.representations.neural.model_artifacts",
521
+ "must contain safe names and lowercase SHA-256 digests",
522
+ )
523
+
524
+
525
+ def _require_digest(value: Any, path: str, code: str) -> None:
526
+ if not isinstance(value, str) or _SHA256.fullmatch(value) is None:
527
+ raise RetrievalManifestError(code, path, "must be a lowercase SHA-256 digest")
528
+
529
+
530
+ def backend_descriptor_sha256(descriptor: BackendDescriptor) -> str:
531
+ """Digest the complete backend identity sealed into each v2 generation."""
532
+
533
+ model_pin_sha256: str | None = None
534
+ model_artifacts: tuple[tuple[str, str], ...] = ()
535
+ if isinstance(descriptor, PinnedLocalBackendDescriptor):
536
+ model_pin_sha256 = descriptor.pin_sha256
537
+ model_artifacts = descriptor.artifact_sha256s
538
+ return _backend_identity_sha256(
539
+ backend_coordinate=descriptor.coordinate,
540
+ backend_id=descriptor.backend_id,
541
+ backend_version=descriptor.backend_version,
542
+ dimensions=descriptor.dimensions,
543
+ quantization=descriptor.quantization,
544
+ query_safe=descriptor.query_safe,
545
+ model_pin_sha256=model_pin_sha256,
546
+ model_artifacts=model_artifacts,
547
+ )
548
+
549
+
550
+ def backend_descriptor_bytes(descriptor: BackendDescriptor) -> bytes:
551
+ """Canonical authenticated v2 generation metadata for one strict descriptor."""
552
+
553
+ return _descriptor_metadata_bytes(backend_descriptor_sha256(descriptor))
554
+
555
+
556
+ def _backend_identity_sha256(
557
+ *,
558
+ backend_coordinate: str,
559
+ backend_id: str,
560
+ backend_version: str,
561
+ dimensions: int,
562
+ quantization: str,
563
+ query_safe: bool,
564
+ model_pin_sha256: str | None,
565
+ model_artifacts: tuple[tuple[str, str], ...],
566
+ ) -> str:
567
+ return sha256_bytes(
568
+ canonical_json_bytes(
569
+ {
570
+ "backend_coordinate": backend_coordinate,
571
+ "backend_id": backend_id,
572
+ "backend_version": backend_version,
573
+ "dimensions": dimensions,
574
+ "model_artifacts": [
575
+ {"path": path, "sha256": digest} for path, digest in model_artifacts
576
+ ],
577
+ "model_pin_sha256": model_pin_sha256,
578
+ "quantization": quantization,
579
+ "query_safe": query_safe,
580
+ }
581
+ )
582
+ )
583
+
584
+
585
+ def _descriptor_metadata_bytes(descriptor_sha256: str) -> bytes:
586
+ return canonical_json_bytes(
587
+ {
588
+ "backend_descriptor_sha256": descriptor_sha256,
589
+ "schema_version": REPRESENTATION_DESCRIPTOR_SCHEMA,
590
+ }
591
+ )
592
+
593
+
594
+ def _representation_descriptor_bytes(representation: RetrievalRepresentation) -> bytes:
595
+ return _descriptor_metadata_bytes(
596
+ _backend_identity_sha256(
597
+ backend_coordinate=representation.backend_coordinate,
598
+ backend_id=representation.backend_id,
599
+ backend_version=representation.backend_version,
600
+ dimensions=representation.dimensions,
601
+ quantization=representation.quantization,
602
+ query_safe=representation.query_safe,
603
+ model_pin_sha256=representation.model_pin_sha256,
604
+ model_artifacts=representation.model_artifacts,
605
+ )
606
+ )
607
+
608
+
609
+ @dataclass(frozen=True)
610
+ class LoadedRetrievalRepresentation:
611
+ """A fully verified sealed representation and its explicitly supplied encoder."""
612
+
613
+ entries: tuple[CatalogEntry, ...]
614
+ vectors: dict[tuple[str, str], tuple[int, ...]]
615
+ backend: EmbeddingBackend
616
+
617
+
618
+ @dataclass(frozen=True)
619
+ class _VerifiedRetrievalRepresentation:
620
+ """One representation whose inner manifest and vector closure have been authenticated."""
621
+
622
+ entries: tuple[CatalogEntry, ...]
623
+ vectors: dict[tuple[str, str], tuple[int, ...]]
624
+
625
+
626
+ @dataclass(frozen=True)
627
+ class LoadedRetrievalCatalog:
628
+ """The verified v2 envelope, with an optional locally resolved neural representation."""
629
+
630
+ manifest: RetrievalManifestV2
631
+ lexical: LoadedRetrievalRepresentation
632
+ neural: LoadedRetrievalRepresentation | None
633
+ fallback_reason: str | None
634
+
635
+
636
+ def load_retrieval_catalog(
637
+ directory: Path,
638
+ *,
639
+ expected_manifest_sha256: str,
640
+ neural_backend: EmbeddingBackend | None = None,
641
+ ) -> LoadedRetrievalCatalog:
642
+ """Verify the complete outer closure before exposing either representation.
643
+
644
+ Neural resolution is deliberately explicit. Omitting the exact local backend never causes
645
+ ambient model discovery: a hybrid artifact then opens as a typed lexical-only runtime view.
646
+ """
647
+
648
+ if not isinstance(directory, Path):
649
+ raise RetrievalManifestError("TYPE", "directory", "must be a Path")
650
+ _require_digest(
651
+ expected_manifest_sha256,
652
+ "retrieval.expected_manifest_sha256",
653
+ "RETRIEVAL_MANIFEST_DIGEST",
654
+ )
655
+ root_fd = _open_directory(directory, label="retrieval root")
656
+ try:
657
+ raw_manifest = _read_regular(
658
+ root_fd,
659
+ (RETRIEVAL_MANIFEST_FILENAME,),
660
+ maximum=MAX_RETRIEVAL_MANIFEST_BYTES,
661
+ )
662
+ if sha256_bytes(raw_manifest) != expected_manifest_sha256:
663
+ raise RetrievalManifestError(
664
+ "RETRIEVAL_MANIFEST_DIGEST",
665
+ RETRIEVAL_MANIFEST_FILENAME,
666
+ "published manifest differs from the required digest",
667
+ )
668
+ manifest = RetrievalManifestV2.from_bytes(raw_manifest)
669
+ captured: dict[str, bytes] = {}
670
+ for representation in manifest.representations:
671
+ for member in representation.members:
672
+ raw = _read_regular(
673
+ root_fd,
674
+ tuple(member.path.split("/")),
675
+ maximum=member.size,
676
+ )
677
+ if len(raw) != member.size or sha256_bytes(raw) != member.sha256:
678
+ raise RetrievalManifestError(
679
+ "RETRIEVAL_MEMBER_TAMPER",
680
+ member.path,
681
+ "member size or digest differs from the outer manifest",
682
+ )
683
+ captured[member.path] = raw
684
+
685
+ verified_lexical = _verify_representation(manifest.lexical, captured=captured)
686
+ verified_neural = (
687
+ None
688
+ if manifest.neural is None
689
+ else _verify_representation(manifest.neural, captured=captured)
690
+ )
691
+ _require_shared_facts(manifest, verified_lexical, verified_neural)
692
+
693
+ lexical = _bind_representation_backend(
694
+ manifest.lexical,
695
+ verified=verified_lexical,
696
+ backend=LexicalHashingBackend(),
697
+ )
698
+ neural: LoadedRetrievalRepresentation | None = None
699
+ fallback_reason = manifest.fallback_reason
700
+ if manifest.neural is not None and neural_backend is not None:
701
+ assert verified_neural is not None
702
+ neural = _bind_representation_backend(
703
+ manifest.neural,
704
+ verified=verified_neural,
705
+ backend=neural_backend,
706
+ )
707
+ elif manifest.neural is not None:
708
+ fallback_reason = "NEURAL_RUNTIME_UNAVAILABLE"
709
+
710
+ return LoadedRetrievalCatalog(
711
+ manifest=manifest,
712
+ lexical=lexical,
713
+ neural=neural,
714
+ fallback_reason=fallback_reason,
715
+ )
716
+ finally:
717
+ os.close(root_fd)
718
+
719
+
720
+ def _bind_representation_backend(
721
+ representation: RetrievalRepresentation,
722
+ *,
723
+ verified: _VerifiedRetrievalRepresentation,
724
+ backend: EmbeddingBackend,
725
+ ) -> LoadedRetrievalRepresentation:
726
+ descriptor = backend.descriptor
727
+ expected_descriptor = (
728
+ representation.backend_id,
729
+ representation.backend_version,
730
+ representation.dimensions,
731
+ representation.quantization,
732
+ representation.query_safe,
733
+ representation.backend_coordinate,
734
+ )
735
+ observed_descriptor = (
736
+ descriptor.backend_id,
737
+ descriptor.backend_version,
738
+ descriptor.dimensions,
739
+ descriptor.quantization,
740
+ descriptor.query_safe,
741
+ descriptor.coordinate,
742
+ )
743
+ if observed_descriptor != expected_descriptor:
744
+ raise RetrievalManifestError(
745
+ "RETRIEVAL_BACKEND_MISMATCH",
746
+ f"retrieval.representations.{representation.name}.backend",
747
+ "the supplied encoder does not match the exact sealed coordinate",
748
+ )
749
+ if representation.name == NEURAL_REPRESENTATION:
750
+ if not isinstance(descriptor, PinnedLocalBackendDescriptor):
751
+ raise RetrievalManifestError(
752
+ "RETRIEVAL_BACKEND_MISMATCH",
753
+ "retrieval.representations.neural.backend",
754
+ "neural loading requires an explicit pinned local descriptor",
755
+ )
756
+ if descriptor.artifact_sha256s != representation.model_artifacts:
757
+ raise RetrievalManifestError(
758
+ "RETRIEVAL_BACKEND_MISMATCH",
759
+ "retrieval.representations.neural.model_artifacts",
760
+ "the supplied encoder artifact closure differs",
761
+ )
762
+ if descriptor.pin_sha256 != representation.model_pin_sha256:
763
+ raise RetrievalManifestError(
764
+ "RETRIEVAL_BACKEND_MISMATCH",
765
+ "retrieval.representations.neural.model_pin_sha256",
766
+ "the supplied encoder PIN digest differs",
767
+ )
768
+ return LoadedRetrievalRepresentation(
769
+ entries=verified.entries,
770
+ vectors=verified.vectors,
771
+ backend=backend,
772
+ )
773
+
774
+
775
+ def _verify_representation(
776
+ representation: RetrievalRepresentation,
777
+ *,
778
+ captured: dict[str, bytes],
779
+ ) -> _VerifiedRetrievalRepresentation:
780
+
781
+ catalog_members = [
782
+ item for item in representation.members if item.path.endswith("/catalog.json")
783
+ ]
784
+ catalog_path = catalog_members[0].path
785
+ try:
786
+ payload = parse_canonical_json(captured[catalog_path])
787
+ except (CanonicalJSONError, TypeError, ValueError) as error:
788
+ raise RetrievalManifestError(
789
+ "RETRIEVAL_REPRESENTATION_MANIFEST",
790
+ catalog_path,
791
+ "inner v1 manifest is not canonical JSON",
792
+ ) from error
793
+ if (
794
+ not isinstance(payload, dict)
795
+ or set(payload) != {"entries", "schema_version"}
796
+ or payload["schema_version"] != CATALOG_INDEX_CONTRACT_VERSION
797
+ or not isinstance(payload["entries"], list)
798
+ ):
799
+ raise RetrievalManifestError(
800
+ "RETRIEVAL_REPRESENTATION_MANIFEST",
801
+ catalog_path,
802
+ "inner v1 manifest fields differ",
803
+ )
804
+ try:
805
+ entries = tuple(catalog_entry_from_dict(item) for item in payload["entries"])
806
+ except (SourceContractError, TypeError, ValueError) as error:
807
+ raise RetrievalManifestError(
808
+ "RETRIEVAL_REPRESENTATION_MANIFEST",
809
+ catalog_path,
810
+ "inner entries violate the v1 catalog contract",
811
+ ) from error
812
+ if tuple(sorted(entries, key=lambda item: (item.entry_id, item.entry_version))) != entries:
813
+ raise RetrievalManifestError(
814
+ "RETRIEVAL_REPRESENTATION_MANIFEST",
815
+ catalog_path,
816
+ "inner entries are not in canonical order",
817
+ )
818
+ if len({entry.entry_id for entry in entries}) != len(entries):
819
+ raise RetrievalManifestError(
820
+ "RETRIEVAL_REPRESENTATION_MANIFEST",
821
+ catalog_path,
822
+ "inner entry ids are not unique",
823
+ )
824
+
825
+ prefix = catalog_path[: -len("catalog.json")]
826
+ descriptor_path = f"{prefix}{REPRESENTATION_DESCRIPTOR_FILENAME}"
827
+ try:
828
+ descriptor_raw = captured[descriptor_path]
829
+ except KeyError:
830
+ raise RetrievalManifestError(
831
+ "RETRIEVAL_REPRESENTATION_MEMBER",
832
+ descriptor_path,
833
+ "representation descriptor is absent from the outer closure",
834
+ ) from None
835
+ if descriptor_raw != _representation_descriptor_bytes(representation):
836
+ raise RetrievalManifestError(
837
+ "RETRIEVAL_BACKEND_MISMATCH",
838
+ descriptor_path,
839
+ "generation descriptor differs from the exact outer backend identity",
840
+ )
841
+ required_paths = {catalog_path, descriptor_path}
842
+ vectors: dict[tuple[str, str], tuple[int, ...]] = {}
843
+ for entry in entries:
844
+ if tuple(sorted(reference.layer for reference in entry.vectors)) != tuple(
845
+ sorted(EMBEDDING_LAYERS)
846
+ ):
847
+ raise RetrievalManifestError(
848
+ "RETRIEVAL_REPRESENTATION_VECTOR",
849
+ entry.coordinate,
850
+ "each entry must bind exactly one vector per layer",
851
+ )
852
+ for reference in entry.vectors:
853
+ if reference.entry_facts_digest != entry.facts_digest:
854
+ raise RetrievalManifestError(
855
+ "RETRIEVAL_REPRESENTATION_VECTOR",
856
+ f"{entry.coordinate}.{reference.layer}",
857
+ "vector is bound to different entry facts",
858
+ )
859
+ if (
860
+ reference.backend_id,
861
+ reference.backend_version,
862
+ reference.dimensions,
863
+ reference.quantization,
864
+ ) != (
865
+ representation.backend_id,
866
+ representation.backend_version,
867
+ representation.dimensions,
868
+ representation.quantization,
869
+ ):
870
+ raise RetrievalManifestError(
871
+ "RETRIEVAL_BACKEND_MISMATCH",
872
+ f"{entry.coordinate}.{reference.layer}",
873
+ "inner vector coordinate differs from the outer representation",
874
+ )
875
+ member_path = f"{prefix}vectors/{reference.vector_sha256}.vec"
876
+ required_paths.add(member_path)
877
+ try:
878
+ vector_raw = captured[member_path]
879
+ except KeyError:
880
+ raise RetrievalManifestError(
881
+ "RETRIEVAL_REPRESENTATION_MEMBER",
882
+ member_path,
883
+ "inner manifest references a member absent from the outer closure",
884
+ ) from None
885
+ if len(vector_raw) != representation.dimensions * 4:
886
+ raise RetrievalManifestError(
887
+ "RETRIEVAL_REPRESENTATION_VECTOR",
888
+ member_path,
889
+ "vector byte length differs from its dimensions",
890
+ )
891
+ if sha256_bytes(vector_raw) != reference.vector_sha256:
892
+ raise RetrievalManifestError(
893
+ "RETRIEVAL_REPRESENTATION_VECTOR",
894
+ member_path,
895
+ "vector bytes differ from the digest bound by the inner manifest",
896
+ )
897
+ vectors[(entry.entry_id, reference.layer)] = struct.unpack(
898
+ f">{representation.dimensions}i", vector_raw
899
+ )
900
+ declared_paths = {item.path for item in representation.members}
901
+ if declared_paths != required_paths:
902
+ raise RetrievalManifestError(
903
+ "RETRIEVAL_REPRESENTATION_MEMBER",
904
+ f"retrieval.representations.{representation.name}.members",
905
+ "outer closure has missing or unreferenced members",
906
+ )
907
+ return _VerifiedRetrievalRepresentation(entries=entries, vectors=vectors)
908
+
909
+
910
+ def _require_shared_facts(
911
+ manifest: RetrievalManifestV2,
912
+ lexical: _VerifiedRetrievalRepresentation,
913
+ neural: _VerifiedRetrievalRepresentation | None,
914
+ ) -> None:
915
+ facts = tuple((entry.coordinate, entry.facts_digest) for entry in lexical.entries)
916
+ expected = sha256_bytes(
917
+ canonical_json_bytes(
918
+ [{"coordinate": coordinate, "facts_sha256": digest} for coordinate, digest in facts]
919
+ )
920
+ )
921
+ if expected != manifest.shared_facts_sha256:
922
+ raise RetrievalManifestError(
923
+ "RETRIEVAL_SHARED_FACTS",
924
+ "retrieval.shared_facts_sha256",
925
+ "lexical facts differ from the shared facts binding",
926
+ )
927
+ if neural is not None:
928
+ neural_facts = tuple((entry.coordinate, entry.facts_digest) for entry in neural.entries)
929
+ if neural_facts != facts:
930
+ raise RetrievalManifestError(
931
+ "RETRIEVAL_SHARED_FACTS",
932
+ "retrieval.representations.neural",
933
+ "neural and lexical representations do not seal identical facts",
934
+ )
935
+
936
+
937
+ def _open_directory(path: Path, *, label: str, dir_fd: int | None = None) -> int:
938
+ flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0)
939
+ try:
940
+ descriptor = os.open(path, flags, dir_fd=dir_fd)
941
+ except OSError as error:
942
+ raise RetrievalManifestError(
943
+ "RETRIEVAL_MEMBER_TAMPER", label, "directory cannot be opened without following links"
944
+ ) from error
945
+ info = os.fstat(descriptor)
946
+ if not stat.S_ISDIR(info.st_mode):
947
+ os.close(descriptor)
948
+ raise RetrievalManifestError("RETRIEVAL_MEMBER_TAMPER", label, "must be a directory")
949
+ return descriptor
950
+
951
+
952
+ def _read_regular(root_fd: int, parts: tuple[str, ...], *, maximum: int) -> bytes:
953
+ owned: list[int] = []
954
+ parent_fd = root_fd
955
+ try:
956
+ for part in parts[:-1]:
957
+ child = _open_directory(Path(part), label="/".join(parts), dir_fd=parent_fd)
958
+ owned.append(child)
959
+ parent_fd = child
960
+ flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_CLOEXEC", 0)
961
+ try:
962
+ descriptor = os.open(parts[-1], flags, dir_fd=parent_fd)
963
+ except OSError as error:
964
+ raise RetrievalManifestError(
965
+ "RETRIEVAL_MEMBER_TAMPER", "/".join(parts), "member cannot be opened safely"
966
+ ) from error
967
+ try:
968
+ before = os.fstat(descriptor)
969
+ if not stat.S_ISREG(before.st_mode) or before.st_nlink != 1 or before.st_size > maximum:
970
+ raise RetrievalManifestError(
971
+ "RETRIEVAL_MEMBER_TAMPER",
972
+ "/".join(parts),
973
+ "member is not a bounded single-link regular file",
974
+ )
975
+ raw = bytearray()
976
+ while len(raw) <= maximum:
977
+ chunk = os.read(descriptor, min(1024 * 1024, maximum + 1 - len(raw)))
978
+ if not chunk:
979
+ break
980
+ raw.extend(chunk)
981
+ after = os.fstat(descriptor)
982
+ if len(raw) > maximum or (before.st_dev, before.st_ino, before.st_size) != (
983
+ after.st_dev,
984
+ after.st_ino,
985
+ after.st_size,
986
+ ):
987
+ raise RetrievalManifestError(
988
+ "RETRIEVAL_MEMBER_TAMPER", "/".join(parts), "member changed during read"
989
+ )
990
+ return bytes(raw)
991
+ finally:
992
+ os.close(descriptor)
993
+ finally:
994
+ for descriptor in reversed(owned):
995
+ os.close(descriptor)