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,1838 @@
1
+ """``mr-data catalog-publish``: classify one authored generation and publish it, once.
2
+
3
+ The third and last provider-scale mutation. It is handed the two receipts the first two stages
4
+ wrote, together with the fingerprints of their exact bytes, and it refuses to start unless those
5
+ two documents describe one staging checkpoint and one authored generation that is complete. It
6
+ then classifies the sweep against its predecessor identity/history, publishes one range-local
7
+ packed generation, has an independent final reader scan the installed generation back off disk,
8
+ and writes one composite receipt.
9
+
10
+ That composite receipt is the authoritative evidence. It *carries* the exact harvest and author
11
+ receipts it was handed rather than a summary of them, binds the execution coordinate, the staging
12
+ indexes, the policy, the classification, every range, vector, posting and bound digest the
13
+ publication produced, the ordered per-invocation counter records and their totals, the expected
14
+ final head, and the independent readback. Downstream verification reads that document; it never
15
+ reconstructs one from counts.
16
+
17
+ Carrying a receipt is not believing it. Every summary in the author receipt is re-derived from
18
+ the authored manifest and the registered policy, and the three members that are not summaries --
19
+ the retained work journal's fingerprint, the bounds the authoring invocation ran under, and the
20
+ bytes it wrote -- are reconciled against the journal and the shards still on disk in the author
21
+ root, as far as retained evidence reaches. It reaches an exact value for one of them, an
22
+ interval for another, and a floor for the rest; the composite states which of those it got for
23
+ each, rather than carrying any of them as though this command had established it.
24
+
25
+ A publication that stops on a bound its caller stated, or on an operator's interruption, writes
26
+ the other receipt this command can write: one typed-incomplete composite receipt bound to the work
27
+ journal the publisher left behind, to this output root, to the ranges that are already immutable,
28
+ and to the ordered per-invocation counter records the work got as far as. ``--resume`` validates
29
+ exactly that receipt, requires the journal on disk to still be the one it names, continues the
30
+ publisher's own resume path, and replaces the receipt with the complete one.
31
+
32
+ A publication that is *killed* rather than stopped -- power, sleep, the kernel -- never writes
33
+ that receipt, because nothing in it runs again. Before the packed head becomes visible, the
34
+ publisher installs a terminal record that the head binds and that captures the exact final journal
35
+ and packed-receipt inputs. ``--resume`` can therefore adopt either unfinished range work or a
36
+ published head whose composite receipt is still absent, after requiring the retained state to be
37
+ this argv's exact publication. The composite an adopted resume ends with is the same document any
38
+ other publication writes. A resume with no retained publication is refused rather than started,
39
+ and a complete receipt is never replaced.
40
+
41
+ This command reaches no network. It takes no key, no endpoint, no URL and no header, and its
42
+ import closure holds nothing from ``acquisition``.
43
+ """
44
+
45
+ from __future__ import annotations
46
+
47
+ import argparse
48
+ import contextlib
49
+ import os
50
+ from collections.abc import Mapping, Sequence
51
+ from datetime import UTC, datetime
52
+ from pathlib import Path
53
+ from typing import Any
54
+
55
+ from mostlyright.data_harness.canonical import canonical_sha256, sha256_bytes
56
+ from mostlyright.data_harness.sources.catalog.authoring_policy import resolve_authoring_policy
57
+ from mostlyright.data_harness.sources.catalog.authoring_shards import (
58
+ AUTHORING_JOURNAL_SCHEMA,
59
+ AUTHORING_MANIFEST_SCHEMA,
60
+ AUTHORING_RECEIPT_SCHEMA,
61
+ JOURNAL_FILENAME,
62
+ MANIFEST_FILENAME,
63
+ MAX_AUTHORING_JOURNAL_BYTES,
64
+ MAX_AUTHORING_MANIFEST_BYTES,
65
+ MAX_AUTHORING_SHARD_BYTES,
66
+ MAX_AUTHORING_SHARD_ENTRIES,
67
+ shard_descriptor_is_valid,
68
+ )
69
+ from mostlyright.data_harness.sources.catalog.contracts import EMBEDDING_LAYERS
70
+ from mostlyright.data_harness.sources.catalog.coverage import coverage_is_valid
71
+ from mostlyright.data_harness.sources.catalog.embedding import LexicalHashingBackend
72
+ from mostlyright.data_harness.sources.catalog.generation_receipt import (
73
+ GENERATION_RECEIPT_SCHEMA,
74
+ HARVEST_RECEIPT_SCHEMA,
75
+ MAX_COMPOSITE_ARRAY_ITEMS,
76
+ MAX_COMPOSITE_RECEIPT_BYTES,
77
+ MAX_PREDECESSOR_HEAD_BYTES,
78
+ MAX_PREDECESSOR_HEAD_DEPTH,
79
+ MAX_PREDECESSOR_RANGE_DESCRIPTORS,
80
+ MAX_STAGE_RECEIPT_MEMBERS,
81
+ ReceiptInstallation,
82
+ compose_generation_receipt,
83
+ install_receipt,
84
+ is_digest,
85
+ load_bounded_json,
86
+ load_bounded_json_at,
87
+ load_execution_coordinate,
88
+ load_stage_receipt,
89
+ publication_output_lock,
90
+ receipt_installation_lock,
91
+ require_output_root_identity,
92
+ retained_child_directory,
93
+ retained_incomplete_receipt,
94
+ retained_input_directory,
95
+ root_coordinate,
96
+ )
97
+ from mostlyright.data_harness.sources.catalog.identity_history import (
98
+ IDENTITY_MANIFEST_FILENAME,
99
+ IDENTITY_MANIFEST_SCHEMA,
100
+ )
101
+ from mostlyright.data_harness.sources.catalog.packed_catalog import (
102
+ COUNTER_MEMBERS,
103
+ MAX_RANGE_DESCRIPTORS,
104
+ PACKED_HEAD_FILENAME,
105
+ RANGE_ENTRIES,
106
+ verify_packed_generation,
107
+ )
108
+ from mostlyright.data_harness.sources.catalog.packed_writer import (
109
+ MAX_PACKED_JOURNAL_BYTES,
110
+ PACKED_JOURNAL_FILENAME,
111
+ PACKED_JOURNAL_SCHEMA,
112
+ PackedLimits,
113
+ admit_authored_generation,
114
+ publish_packed_generation,
115
+ verify_packed_receipt,
116
+ )
117
+ from mostlyright.data_harness.sources.catalog.streaming_delta import (
118
+ DELTA_CLASSIFICATIONS,
119
+ DELTA_IDENTITY_DIRNAME,
120
+ DELTA_MANIFEST_FILENAME,
121
+ MAX_DELTA_MANIFEST_BYTES,
122
+ StreamingDeltaLimits,
123
+ SweepMeasures,
124
+ run_streaming_delta,
125
+ )
126
+ from mostlyright.data_harness.sources.contracts import SourceContractError
127
+ from mostlyright.data_harness.ux.commands import CommandSpec
128
+
129
+ PUBLISH_INCOMPLETE_STATUS = "generation_incomplete"
130
+ PUBLISH_COMPLETE_STATUS = "generation_published"
131
+
132
+ #: The refusals that mean *stopped*, not *wrong*: a publication that reached a bound its caller
133
+ #: stated leaves every completed range durable and a typed-incomplete work journal beside them.
134
+ #: Anything else is a defect in the generation and is raised.
135
+ PUBLICATION_INTERRUPTIONS = frozenset({"PACKED_DISK_LIMIT", "PACKED_WALL_LIMIT"})
136
+
137
+ #: What an operator's interruption is recorded as. A budget records the bound it reached instead.
138
+ PUBLISH_INTERRUPTED_REASON = "PUBLISH_INTERRUPTED"
139
+
140
+ #: The two backend coordinates this command admits, spelled the way the closed argv spells them.
141
+ LEXICAL_BACKENDS = ("lexical-hash-v1",)
142
+ NEURAL_BACKENDS = ("minilm-v1",)
143
+
144
+ #: Directories this command owns inside its own output root.
145
+ DELTA_DIRNAME = "delta"
146
+
147
+ #: Every predecessor coordinate, and the mode that requires all six of them.
148
+ _PREDECESSOR_ARGUMENTS = (
149
+ "predecessor_identity_root",
150
+ "predecessor_identity_digest",
151
+ "predecessor_history_root",
152
+ "predecessor_history_digest",
153
+ "predecessor_catalog_root",
154
+ "predecessor_catalog_digest",
155
+ )
156
+
157
+ #: The fixed shard/range shape this command may state but never widen.
158
+ _FIXED = {
159
+ "max_ranges": MAX_RANGE_DESCRIPTORS,
160
+ "max_range_entries": RANGE_ENTRIES,
161
+ "max_facts_bytes": 8_388_608,
162
+ "max_member_bytes": 8_388_608,
163
+ "max_posting_segments": 8,
164
+ "max_open_files": 16,
165
+ }
166
+
167
+ _AUTHOR_RECEIPT_MEMBERS = frozenset(
168
+ {
169
+ "schema_version",
170
+ "status",
171
+ "reason_code",
172
+ "policy_id",
173
+ "policy_sha256",
174
+ "provider_id",
175
+ "harvester_coordinate",
176
+ "staging",
177
+ "coverage",
178
+ "counts",
179
+ "layer_text_digests",
180
+ "shard_count",
181
+ "shards",
182
+ "limits",
183
+ "manifest_sha256",
184
+ "journal_sha256",
185
+ "output_bytes",
186
+ "harvest_receipt_sha256",
187
+ "output_root_sha256",
188
+ "execution",
189
+ }
190
+ )
191
+ _AUTHOR_MANIFEST_MEMBERS = frozenset(
192
+ {
193
+ "schema_version",
194
+ "policy_id",
195
+ "policy_sha256",
196
+ "staging",
197
+ "coverage",
198
+ "counts",
199
+ "layer_text_digests",
200
+ "shards",
201
+ "root_sha256",
202
+ }
203
+ )
204
+ _AUTHOR_STAGING_MEMBERS = frozenset(
205
+ {
206
+ "checkpoint_sha256",
207
+ "config_sha256",
208
+ "endpoint",
209
+ "harvester_coordinate",
210
+ "pass_number",
211
+ "pass_map_sha256",
212
+ "pass_map_root_sha256",
213
+ "unique_records",
214
+ "raw_response_index_root_sha256",
215
+ "raw_response_index_count",
216
+ "normalized_page_index_root_sha256",
217
+ "normalized_page_index_count",
218
+ "record_index_root_sha256",
219
+ "record_index_count",
220
+ }
221
+ )
222
+ _AUTHOR_COUNT_MEMBERS = frozenset(
223
+ {"records", "authored", "flagged", "skipped", "failed", "layer_text_truncated"}
224
+ )
225
+ _AUTHOR_LIMIT_MEMBERS = frozenset(
226
+ {
227
+ "max_records",
228
+ "max_input_bytes",
229
+ "max_shard_entries",
230
+ "max_shard_bytes",
231
+ "max_disk_bytes",
232
+ "max_wall_seconds",
233
+ }
234
+ )
235
+ _AUTHOR_JOURNAL_MEMBERS = frozenset(
236
+ {
237
+ "schema_version",
238
+ "policy_sha256",
239
+ "staging_checkpoint_sha256",
240
+ "coverage",
241
+ "shards",
242
+ "counts",
243
+ "layer_text_digests",
244
+ "last_provider_record_id",
245
+ "reason_code",
246
+ }
247
+ )
248
+
249
+ #: Exactly how far this command got with each author-receipt member that is *not* a manifest
250
+ #: summary. Every one of these used to be accepted on its own shape, and reconciling them is
251
+ #: what this command now does; but three of them can only be bounded and three cannot be reached
252
+ #: at all, and a composite that named only the unreachable ones would tell a reader the bounded
253
+ #: ones had been established. So the composite states the standing of each, in these four terms:
254
+ #:
255
+ #: ``bound`` re-derived exactly from evidence retained in the author root.
256
+ #: ``bounded_interval`` reconciled against an interval the retained outputs allow rather than an
257
+ #: exact value, because the member measures one invocation of a stage that
258
+ #: may have run more than once.
259
+ #: ``bounded_below`` the authored generation proves the bound was at least this wide. Nothing
260
+ #: retained can show it was not stated wider than the invocation ran under.
261
+ #: ``unverified`` bounds work rather than output. No evidence in the author root reaches
262
+ #: it, and this command does not pretend otherwise.
263
+ AUTHOR_CLAIM_RECONCILIATION = {
264
+ "journal_sha256": "bound",
265
+ "limits.max_disk_bytes": "unverified",
266
+ "limits.max_input_bytes": "unverified",
267
+ "limits.max_records": "bounded_below",
268
+ "limits.max_shard_bytes": "bounded_below",
269
+ "limits.max_shard_entries": "bounded_below",
270
+ "limits.max_wall_seconds": "unverified",
271
+ "output_bytes": "bounded_interval",
272
+ }
273
+
274
+
275
+ def add_arguments(parser: argparse.ArgumentParser) -> None:
276
+ parser.add_argument(
277
+ "--author-root", type=Path, required=True, help="the authored generation's shard root"
278
+ )
279
+ parser.add_argument(
280
+ "--author-receipt", type=Path, required=True, help="the exact authoring stage receipt"
281
+ )
282
+ parser.add_argument(
283
+ "--author-receipt-sha256", required=True, help="the exact authoring receipt fingerprint"
284
+ )
285
+ parser.add_argument(
286
+ "--harvest-receipt", type=Path, required=True, help="the exact harvest stage receipt"
287
+ )
288
+ parser.add_argument(
289
+ "--harvest-receipt-sha256", required=True, help="the exact harvest receipt fingerprint"
290
+ )
291
+ parser.add_argument(
292
+ "--output-root", type=Path, required=True, help="the packed catalog output root"
293
+ )
294
+ parser.add_argument(
295
+ "--receipt-file",
296
+ type=Path,
297
+ required=True,
298
+ help="where this generation's one composite receipt is installed",
299
+ )
300
+ parser.add_argument(
301
+ "--execution-manifest",
302
+ type=Path,
303
+ required=True,
304
+ help="the committed execution coordinate document",
305
+ )
306
+ parser.add_argument(
307
+ "--execution-commit", required=True, help="the exact commit that coordinate was cut at"
308
+ )
309
+ parser.add_argument(
310
+ "--predecessor-mode",
311
+ choices=("none", "exact"),
312
+ required=True,
313
+ help="whether this generation has an exact predecessor, or none at all",
314
+ )
315
+ parser.add_argument(
316
+ "--predecessor-identity-root", type=Path, help="the predecessor identity generation root"
317
+ )
318
+ parser.add_argument(
319
+ "--predecessor-identity-digest", help="the exact predecessor identity manifest fingerprint"
320
+ )
321
+ parser.add_argument(
322
+ "--predecessor-history-root", type=Path, help="the predecessor classification root"
323
+ )
324
+ parser.add_argument(
325
+ "--predecessor-history-digest", help="the exact predecessor delta manifest fingerprint"
326
+ )
327
+ parser.add_argument(
328
+ "--predecessor-catalog-root", type=Path, help="the predecessor packed catalog root"
329
+ )
330
+ parser.add_argument(
331
+ "--predecessor-catalog-digest", help="the exact predecessor packed head fingerprint"
332
+ )
333
+ parser.add_argument(
334
+ "--lexical-backend",
335
+ choices=LEXICAL_BACKENDS,
336
+ required=True,
337
+ help="the closed lexical representation this generation publishes",
338
+ )
339
+ parser.add_argument(
340
+ "--neural-backend",
341
+ choices=NEURAL_BACKENDS,
342
+ help="the closed neural representation this generation also publishes",
343
+ )
344
+ parser.add_argument("--neural-pin", help="the exact pinned model fingerprint")
345
+ parser.add_argument("--model-root", type=Path, help="the governed local model root")
346
+ for name, ceiling in _FIXED.items():
347
+ parser.add_argument(
348
+ f"--{name.replace('_', '-')}",
349
+ type=int,
350
+ required=True,
351
+ help=f"the fixed packed bound, which is exactly {ceiling}",
352
+ )
353
+ parser.add_argument(
354
+ "--max-batch-invocations",
355
+ type=int,
356
+ required=True,
357
+ help="maximum batch encode calls across every backend",
358
+ )
359
+ parser.add_argument(
360
+ "--max-encoded-layer-items",
361
+ type=int,
362
+ required=True,
363
+ help="maximum encoded layer texts across every backend",
364
+ )
365
+ parser.add_argument(
366
+ "--max-backend-scalar-invocations",
367
+ type=int,
368
+ required=True,
369
+ help="maximum backend scalar calls across every backend",
370
+ )
371
+ parser.add_argument(
372
+ "--max-encoded-input-bytes",
373
+ type=int,
374
+ required=True,
375
+ help="maximum encoded UTF-8 input bytes across every backend",
376
+ )
377
+ parser.add_argument(
378
+ "--max-disk-bytes", type=int, required=True, help="maximum local working disk bytes"
379
+ )
380
+ parser.add_argument(
381
+ "--max-wall-seconds", type=int, required=True, help="maximum cumulative publication seconds"
382
+ )
383
+ parser.add_argument(
384
+ "--resume",
385
+ action="store_true",
386
+ help="continue the publication this receipt file was left incomplete at",
387
+ )
388
+
389
+
390
+ def run(args: argparse.Namespace) -> dict[str, Any]:
391
+ _validate_limits(args)
392
+ predecessor = _predecessor_coordinates(args)
393
+ execution = load_execution_coordinate(args.execution_manifest, args.execution_commit)
394
+ output_root = Path(args.output_root)
395
+ harvest_raw, harvest = load_stage_receipt(
396
+ args.harvest_receipt,
397
+ args.harvest_receipt_sha256,
398
+ label="harvest_receipt",
399
+ schemas=frozenset({HARVEST_RECEIPT_SCHEMA}),
400
+ )
401
+ author_raw, author = load_stage_receipt(
402
+ args.author_receipt,
403
+ args.author_receipt_sha256,
404
+ label="author_receipt",
405
+ schemas=frozenset({AUTHORING_RECEIPT_SCHEMA}),
406
+ )
407
+ _verify_handoff(
408
+ harvest,
409
+ author,
410
+ harvest_sha256=sha256_bytes(harvest_raw),
411
+ execution=execution,
412
+ )
413
+ manifest_raw, manifest = _author_manifest(Path(args.author_root), author["manifest_sha256"])
414
+ author_evidence = _verify_author_receipt(
415
+ author,
416
+ manifest=manifest,
417
+ manifest_bytes=len(manifest_raw),
418
+ harvest=harvest,
419
+ harvest_sha256=sha256_bytes(harvest_raw),
420
+ author_root=Path(args.author_root),
421
+ execution=execution,
422
+ )
423
+ admit_authored_generation(Path(args.author_root), manifest)
424
+ predecessor_identity_context = (
425
+ contextlib.nullcontext(None)
426
+ if predecessor is None
427
+ else retained_input_directory(
428
+ predecessor["identity_root"],
429
+ label="predecessor_identity_root.manifest",
430
+ code_prefix="GENERATION",
431
+ refusal=SourceContractError,
432
+ )
433
+ )
434
+ with predecessor_identity_context as predecessor_identity_descriptor:
435
+ predecessor_heads = _read_predecessor_heads(
436
+ predecessor, identity_descriptor=predecessor_identity_descriptor
437
+ )
438
+ with publication_output_lock(
439
+ output_root,
440
+ label="output_root",
441
+ code_prefix="PUBLISH",
442
+ refusal=SourceContractError,
443
+ ) as output_descriptor:
444
+ with receipt_installation_lock(
445
+ args.receipt_file,
446
+ label="receipt_file",
447
+ code_prefix="PUBLISH",
448
+ refusal=SourceContractError,
449
+ ) as receipt_installation:
450
+ return _run_locked(
451
+ args,
452
+ output_descriptor=output_descriptor,
453
+ receipt_installation=receipt_installation,
454
+ predecessor=predecessor,
455
+ predecessor_identity_descriptor=predecessor_identity_descriptor,
456
+ predecessor_heads=predecessor_heads,
457
+ execution=execution,
458
+ output_root=output_root,
459
+ harvest_raw=harvest_raw,
460
+ harvest=harvest,
461
+ author_raw=author_raw,
462
+ author=author,
463
+ manifest=manifest,
464
+ author_evidence=author_evidence,
465
+ )
466
+
467
+
468
+ def _run_locked(
469
+ args: argparse.Namespace,
470
+ *,
471
+ output_descriptor: int,
472
+ receipt_installation: ReceiptInstallation,
473
+ predecessor: dict[str, Any] | None,
474
+ predecessor_identity_descriptor: int | None,
475
+ predecessor_heads: dict[str, Any] | None,
476
+ execution: dict[str, Any],
477
+ output_root: Path,
478
+ harvest_raw: bytes,
479
+ harvest: dict[str, Any],
480
+ author_raw: bytes,
481
+ author: dict[str, Any],
482
+ manifest: dict[str, Any],
483
+ author_evidence: dict[str, Any],
484
+ ) -> dict[str, Any]:
485
+ retained = None
486
+ orphaned_journal = False
487
+ terminal_publication = False
488
+ if args.resume:
489
+ retained = retained_incomplete_receipt(
490
+ args.receipt_file,
491
+ maximum=MAX_COMPOSITE_RECEIPT_BYTES,
492
+ label="receipt_file",
493
+ schema=GENERATION_RECEIPT_SCHEMA,
494
+ incomplete_status=PUBLISH_INCOMPLETE_STATUS,
495
+ output_root=output_root,
496
+ code_prefix="PUBLISH",
497
+ refusal=SourceContractError,
498
+ )
499
+ if retained is None:
500
+ try:
501
+ os.stat(PACKED_HEAD_FILENAME, dir_fd=output_descriptor, follow_symlinks=False)
502
+ terminal_publication = True
503
+ except FileNotFoundError:
504
+ terminal_publication = False
505
+ if (
506
+ _retained_journal(output_descriptor, logical_output_root=output_root) is None
507
+ and not terminal_publication
508
+ ):
509
+ raise SourceContractError(
510
+ "PUBLISH_RESUME_RECEIPT",
511
+ "receipt_file",
512
+ "a resume continues the typed-incomplete receipt its interruption left behind, "
513
+ "or the work journal a publication was killed in the middle of",
514
+ )
515
+ orphaned_journal = not terminal_publication
516
+ else:
517
+ _require_retained_journal(retained, output_descriptor, logical_output_root=output_root)
518
+ elif Path(args.receipt_file).exists():
519
+ raise SourceContractError(
520
+ "PUBLISH_RECEIPT_DESTINATION",
521
+ "receipt_file",
522
+ "receipt destination must be an absent no-follow path",
523
+ )
524
+
525
+ require_output_root_identity(
526
+ output_root,
527
+ output_descriptor,
528
+ label="output_root",
529
+ code_prefix="PUBLISH",
530
+ refusal=SourceContractError,
531
+ )
532
+ delta_root = output_root / DELTA_DIRNAME
533
+ logical_delta_root = output_root / DELTA_DIRNAME
534
+ classification = _classify(
535
+ args,
536
+ author=author,
537
+ harvest=harvest,
538
+ delta_root=delta_root,
539
+ output_descriptor=output_descriptor,
540
+ coordinate_root=logical_delta_root,
541
+ predecessor=predecessor,
542
+ predecessor_identity_descriptor=predecessor_identity_descriptor,
543
+ resumed=args.resume,
544
+ )
545
+ backends = _backends(args)
546
+ # Every block that describes this generation rather than this invocation of it. Both receipts
547
+ # this command can write state exactly these, so an interruption cannot describe its inputs
548
+ # differently from the publication that completes them.
549
+ common: dict[str, Any] = {
550
+ "components": {
551
+ "harvest_receipt": harvest,
552
+ "harvest_receipt_sha256": sha256_bytes(harvest_raw),
553
+ "author_receipt": author,
554
+ "author_receipt_sha256": sha256_bytes(author_raw),
555
+ },
556
+ "execution": execution,
557
+ "staging": author["staging"],
558
+ "policy": {
559
+ "policy_id": author["policy_id"],
560
+ "policy_sha256": author["policy_sha256"],
561
+ "provider_id": author["provider_id"],
562
+ "harvester_coordinate": author["harvester_coordinate"],
563
+ },
564
+ "author": {
565
+ "manifest_sha256": author["manifest_sha256"],
566
+ "root_sha256": manifest["root_sha256"],
567
+ "shard_count": len(manifest["shards"]),
568
+ # What this generation covers, stated in the composite's own summary as well as
569
+ # carried in the author receipt beside it. A reader of the authoritative receipt
570
+ # answers "is this the whole sweep" without opening a component document.
571
+ "coverage": manifest["coverage"],
572
+ "counts": manifest["counts"],
573
+ "layer_text_digests": manifest["layer_text_digests"],
574
+ **author_evidence,
575
+ },
576
+ "classification": classification,
577
+ "predecessor": predecessor_heads,
578
+ "limits": _limits_evidence(args),
579
+ }
580
+ if retained is not None:
581
+ _require_retained_generation(retained, common)
582
+ publication_binding_sha256 = _generation_binding_sha256(common)
583
+ if orphaned_journal:
584
+ _adopt_orphaned_journal(
585
+ author=author,
586
+ predecessor=predecessor,
587
+ output_root=output_root,
588
+ output_descriptor=output_descriptor,
589
+ publication_binding_sha256=publication_binding_sha256,
590
+ )
591
+ try:
592
+ with retained_child_directory(
593
+ output_descriptor,
594
+ DELTA_DIRNAME,
595
+ create=False,
596
+ label="delta.output",
597
+ code_prefix="PUBLISH",
598
+ refusal=SourceContractError,
599
+ ) as delta_descriptor:
600
+ publication = publish_packed_generation(
601
+ delta_root=delta_root,
602
+ delta_descriptor=delta_descriptor,
603
+ expected_delta_manifest_sha256=classification["manifest_sha256"],
604
+ authoring_root=Path(args.author_root),
605
+ expected_authoring_manifest_sha256=author["manifest_sha256"],
606
+ output_root=output_root,
607
+ output_descriptor=output_descriptor,
608
+ backends=backends,
609
+ predecessor_root=None if predecessor is None else predecessor["catalog_root"],
610
+ expected_predecessor_head_sha256=(
611
+ None if predecessor is None else predecessor["catalog_digest"]
612
+ ),
613
+ expected_predecessor_generation_sha256=(
614
+ None if predecessor is None else predecessor["history_digest"]
615
+ ),
616
+ limits=PackedLimits(
617
+ max_ranges=args.max_ranges,
618
+ max_entries=args.max_ranges * args.max_range_entries,
619
+ max_disk_bytes=args.max_disk_bytes,
620
+ max_wall_seconds=args.max_wall_seconds,
621
+ ),
622
+ resume=args.resume,
623
+ publication_binding_sha256=publication_binding_sha256,
624
+ )
625
+ except SourceContractError as refusal:
626
+ if refusal.code not in PUBLICATION_INTERRUPTIONS:
627
+ raise
628
+ require_output_root_identity(
629
+ output_root,
630
+ output_descriptor,
631
+ label="output_root",
632
+ code_prefix="PUBLISH",
633
+ refusal=SourceContractError,
634
+ )
635
+ stopped = _incomplete_receipt(
636
+ args,
637
+ common=common,
638
+ output_root=output_root,
639
+ output_descriptor=output_descriptor,
640
+ reason_code=refusal.code,
641
+ retained=retained,
642
+ receipt_installation=receipt_installation,
643
+ )
644
+ if stopped is None:
645
+ raise
646
+ return stopped
647
+ except KeyboardInterrupt:
648
+ require_output_root_identity(
649
+ output_root,
650
+ output_descriptor,
651
+ label="output_root",
652
+ code_prefix="PUBLISH",
653
+ refusal=SourceContractError,
654
+ )
655
+ stopped = _incomplete_receipt(
656
+ args,
657
+ common=common,
658
+ output_root=output_root,
659
+ output_descriptor=output_descriptor,
660
+ reason_code=PUBLISH_INTERRUPTED_REASON,
661
+ retained=retained,
662
+ receipt_installation=receipt_installation,
663
+ )
664
+ if stopped is None:
665
+ raise
666
+ return stopped
667
+ receipt = publication.receipt
668
+ _verify_work_budget(args, receipt)
669
+ verify_packed_receipt(receipt, root=output_root, root_descriptor=output_descriptor)
670
+ installed = verify_packed_generation(
671
+ output_root,
672
+ expected_head_sha256=publication.head_sha256,
673
+ root_descriptor=output_descriptor,
674
+ )
675
+
676
+ payload = compose_generation_receipt(
677
+ {
678
+ "schema_version": GENERATION_RECEIPT_SCHEMA,
679
+ "status": PUBLISH_COMPLETE_STATUS,
680
+ "reason_code": None,
681
+ "published_at": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
682
+ "output_root_sha256": root_coordinate(output_root),
683
+ **common,
684
+ "publication": _publication_evidence(receipt, publication, backends),
685
+ "readback": {
686
+ "reader": "packed_catalog.verify_packed_generation",
687
+ "head_sha256": installed.head_sha256,
688
+ "range_count": installed.range_count,
689
+ "entry_count": installed.entry_count,
690
+ "member_count": installed.member_count,
691
+ "posting_term_count": installed.posting_term_count,
692
+ "backends": list(installed.backends),
693
+ "readback_sha256": canonical_sha256(
694
+ {
695
+ "backends": list(installed.backends),
696
+ "entry_count": installed.entry_count,
697
+ "head_sha256": installed.head_sha256,
698
+ "member_count": installed.member_count,
699
+ "posting_term_count": installed.posting_term_count,
700
+ "range_count": installed.range_count,
701
+ }
702
+ ),
703
+ },
704
+ }
705
+ )
706
+ require_output_root_identity(
707
+ output_root,
708
+ output_descriptor,
709
+ label="output_root",
710
+ code_prefix="PUBLISH",
711
+ refusal=SourceContractError,
712
+ )
713
+ install_receipt(
714
+ args.receipt_file,
715
+ payload,
716
+ maximum=MAX_COMPOSITE_RECEIPT_BYTES,
717
+ label="receipt_file",
718
+ code_prefix="PUBLISH",
719
+ refusal=SourceContractError,
720
+ replaces=retained,
721
+ installation=receipt_installation,
722
+ commit_guard=lambda: require_output_root_identity(
723
+ output_root,
724
+ output_descriptor,
725
+ label="output_root",
726
+ code_prefix="PUBLISH",
727
+ refusal=SourceContractError,
728
+ ),
729
+ )
730
+ return payload
731
+
732
+
733
+ # ------------------------------------------------------------------------------------------------
734
+ # Stopping: the receipt an interruption leaves, and the resume that continues exactly it
735
+ # ------------------------------------------------------------------------------------------------
736
+
737
+
738
+ def _retained_journal(
739
+ output_descriptor: int, *, logical_output_root: Path
740
+ ) -> tuple[bytes, dict[str, Any]] | None:
741
+ """The typed-incomplete work journal an interruption left in this root, or nothing at all.
742
+
743
+ A journal that is absent, that is complete, or that was written for another root is not
744
+ something to continue: each of those is reported as *no retained publication* rather than
745
+ guessed at, and the caller decides what that means where it stands.
746
+ """
747
+
748
+ try:
749
+ raw, journal = load_bounded_json_at(
750
+ output_descriptor,
751
+ PACKED_JOURNAL_FILENAME,
752
+ label="packed.journal",
753
+ maximum=MAX_PACKED_JOURNAL_BYTES,
754
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
755
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
756
+ max_items=MAX_COMPOSITE_ARRAY_ITEMS,
757
+ )
758
+ except SourceContractError as error:
759
+ if error.code == "GENERATION_INPUT_PATH":
760
+ try:
761
+ os.stat(PACKED_JOURNAL_FILENAME, dir_fd=output_descriptor, follow_symlinks=False)
762
+ except FileNotFoundError:
763
+ return None
764
+ raise
765
+ if (
766
+ journal.get("schema_version") != PACKED_JOURNAL_SCHEMA
767
+ or journal.get("status") != "incomplete"
768
+ or journal.get("head_sha256") is not None
769
+ or journal.get("output_root") != os.path.abspath(os.fspath(logical_output_root))
770
+ ):
771
+ return None
772
+ return raw, journal
773
+
774
+
775
+ def _candidate_journals(output_descriptor: int) -> list[str]:
776
+ """Every document directly inside this output root that claims to be a packed work journal.
777
+
778
+ A publication writes exactly one, under one name. Anything else here -- a copy somebody kept,
779
+ a journal moved aside by hand -- makes *which publication is being continued* a question, and
780
+ a resume answers questions like that by refusing rather than by picking one.
781
+ """
782
+
783
+ names: list[str] = []
784
+ for name in sorted(os.listdir(output_descriptor)):
785
+ try:
786
+ _raw, document = load_bounded_json_at(
787
+ output_descriptor,
788
+ name,
789
+ label="packed.journal",
790
+ maximum=MAX_PACKED_JOURNAL_BYTES,
791
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
792
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
793
+ max_items=MAX_COMPOSITE_ARRAY_ITEMS,
794
+ )
795
+ except SourceContractError:
796
+ # Anything that is not a bounded canonical document is not a work journal. Directories,
797
+ # packs, heads and receipts all land here, which is why this reads rather than lists.
798
+ continue
799
+ if document.get("schema_version") == PACKED_JOURNAL_SCHEMA:
800
+ names.append(name)
801
+ return names
802
+
803
+
804
+ def _adopt_orphaned_journal(
805
+ *,
806
+ author: Mapping[str, Any],
807
+ predecessor: Mapping[str, Any] | None,
808
+ output_root: Path,
809
+ output_descriptor: int,
810
+ publication_binding_sha256: str,
811
+ ) -> None:
812
+ """Continue the publication a hard kill left behind, which left no receipt to continue.
813
+
814
+ A publication that reaches a bound it was given writes down where it got to. One that is
815
+ killed outright -- power, sleep, the kernel -- writes nothing, because nothing in it ever runs
816
+ again: the only state left is the work journal and the immutable members it names. That state
817
+ is adoptable, and for a multi-hour publication it is the difference between continuing and
818
+ starting again. It is adopted only when it is *this* argv's publication, so the journal has to
819
+ bind:
820
+
821
+ * this exact output root, which the journal states and which ``_retained_journal`` checks;
822
+ * one interrupted publication, and not a choice between several;
823
+ * the exact execution, component receipts, staging, policy, authoring, classification,
824
+ predecessor and immutable limit evidence bound before the first packed range was written;
825
+ * the provider and the exact predecessor head this invocation was given.
826
+
827
+ The rest of the generation coordinate -- backends, posting backend, classification and identity
828
+ manifests -- and the exact bytes of every member each completed range names are re-derived by
829
+ the publisher's own resume law, before it does one byte of new work and before anything here
830
+ is written. That law is not copied into this command; a second copy of it would be a second
831
+ thing to keep true.
832
+ """
833
+
834
+ candidates = _candidate_journals(output_descriptor)
835
+ if len(candidates) > 1:
836
+ raise SourceContractError(
837
+ "PUBLISH_RESUME_JOURNAL",
838
+ "packed.journal",
839
+ "this output root holds more than one work journal, and a resume continues exactly one",
840
+ )
841
+ found = _retained_journal(output_descriptor, logical_output_root=output_root)
842
+ if found is None:
843
+ raise SourceContractError(
844
+ "PUBLISH_RESUME_RECEIPT",
845
+ "receipt_file",
846
+ "a resume continues the typed-incomplete receipt its interruption left behind, or the "
847
+ "work journal a publication was killed in the middle of",
848
+ )
849
+ generation = found[1].get("generation")
850
+ stated = {
851
+ "authoring_manifest_sha256": author["manifest_sha256"],
852
+ "publication_binding_sha256": publication_binding_sha256,
853
+ "provider_id": author["provider_id"],
854
+ "predecessor_head_sha256": None if predecessor is None else predecessor["catalog_digest"],
855
+ }
856
+ if not isinstance(generation, Mapping) or any(
857
+ generation.get(member) != value for member, value in stated.items()
858
+ ):
859
+ raise SourceContractError(
860
+ "PUBLISH_RESUME_JOURNAL",
861
+ "packed.journal",
862
+ "the interrupted publication in this output root is of another generation than the "
863
+ "one this invocation was given",
864
+ )
865
+
866
+
867
+ def _require_retained_journal(
868
+ retained: Mapping[str, Any], output_descriptor: int, *, logical_output_root: Path
869
+ ) -> None:
870
+ """A resume continues the exact interrupted publication its receipt was written against."""
871
+
872
+ stated = retained.get("publication")
873
+ stated_sha256 = stated.get("journal_sha256") if isinstance(stated, Mapping) else None
874
+ found = _retained_journal(output_descriptor, logical_output_root=logical_output_root)
875
+ if found is None or not is_digest(stated_sha256) or sha256_bytes(found[0]) != stated_sha256:
876
+ raise SourceContractError(
877
+ "PUBLISH_RESUME_RECEIPT",
878
+ "packed.journal",
879
+ "a resume continues the exact interrupted publication its receipt was left bound to",
880
+ )
881
+
882
+
883
+ def _require_retained_generation(retained: Mapping[str, Any], common: Mapping[str, Any]) -> None:
884
+ """Require every generation-defining block to match the interrupted invocation."""
885
+
886
+ retained_components = retained.get("components")
887
+ current_components = common.get("components")
888
+ if isinstance(retained_components, Mapping) and isinstance(current_components, Mapping):
889
+ for stage in ("author", "harvest"):
890
+ member = f"{stage}_receipt_sha256"
891
+ if retained_components.get(member) != current_components.get(member):
892
+ raise SourceContractError(
893
+ "PUBLISH_RESUME_RECEIPT",
894
+ f"{stage}_receipt",
895
+ "the retained publication was built from another stage receipt",
896
+ )
897
+ for member in (
898
+ "components",
899
+ "execution",
900
+ "staging",
901
+ "policy",
902
+ "author",
903
+ "classification",
904
+ "predecessor",
905
+ ):
906
+ if retained.get(member) != common.get(member):
907
+ raise SourceContractError(
908
+ "PUBLISH_RESUME_RECEIPT",
909
+ member,
910
+ "a resume must use the exact generation evidence retained at interruption",
911
+ )
912
+ retained_limits = retained.get("limits")
913
+ current_limits = common.get("limits")
914
+ mutable = {"max_disk_bytes", "max_wall_seconds"}
915
+ if (
916
+ not isinstance(retained_limits, Mapping)
917
+ or not isinstance(current_limits, Mapping)
918
+ or {key: value for key, value in retained_limits.items() if key not in mutable}
919
+ != {key: value for key, value in current_limits.items() if key not in mutable}
920
+ ):
921
+ raise SourceContractError(
922
+ "PUBLISH_RESUME_RECEIPT",
923
+ "limits",
924
+ "a resume may change only its cumulative disk and wall-time ceilings",
925
+ )
926
+
927
+
928
+ def _generation_binding_sha256(common: Mapping[str, Any]) -> str:
929
+ """Bind immutable generation evidence into the packed work journal."""
930
+
931
+ limits = common.get("limits")
932
+ if not isinstance(limits, Mapping):
933
+ raise SourceContractError(
934
+ "PUBLISH_LIMIT", "limits", "generation limits must be one exact object"
935
+ )
936
+ mutable = {"max_disk_bytes", "max_wall_seconds"}
937
+ return canonical_sha256(
938
+ {
939
+ **common,
940
+ "limits": {key: value for key, value in limits.items() if key not in mutable},
941
+ }
942
+ )
943
+
944
+
945
+ def _journal_evidence(journal: Mapping[str, Any]) -> dict[str, Any] | None:
946
+ """What one typed-incomplete journal states about the work it got as far as.
947
+
948
+ Nothing here is believed because the journal says it: a record whose shape is not the one the
949
+ publisher writes makes this report nothing, and the publication's own refusal is raised
950
+ instead of a receipt describing work that cannot be read.
951
+ """
952
+
953
+ invocations = journal.get("invocations")
954
+ completed = journal.get("completed_ranges")
955
+ generation = journal.get("generation")
956
+ if (
957
+ not isinstance(invocations, list)
958
+ or not invocations
959
+ or not isinstance(completed, list)
960
+ or not isinstance(generation, Mapping)
961
+ ):
962
+ return None
963
+ records: list[dict[str, int]] = []
964
+ for record in invocations:
965
+ if not isinstance(record, Mapping) or set(record) != set(COUNTER_MEMBERS):
966
+ return None
967
+ if any(type(record[name]) is not int or record[name] < 0 for name in COUNTER_MEMBERS):
968
+ return None
969
+ records.append({name: record[name] for name in COUNTER_MEMBERS})
970
+ ranges: list[str] = []
971
+ for descriptor in completed:
972
+ if not isinstance(descriptor, Mapping) or not is_digest(
973
+ descriptor.get("range_manifest_sha256")
974
+ ):
975
+ return None
976
+ ranges.append(descriptor["range_manifest_sha256"])
977
+ backends = generation.get("backends")
978
+ if not isinstance(backends, list) or not all(isinstance(name, str) for name in backends):
979
+ return None
980
+ return {
981
+ "journal_status": "incomplete",
982
+ "completed_range_count": len(ranges),
983
+ "range_manifest_sha256s": ranges,
984
+ "counter_records": records,
985
+ "counters": {name: sum(record[name] for record in records) for name in COUNTER_MEMBERS},
986
+ "backends": list(backends),
987
+ "predecessor": generation.get("predecessor_head_sha256"),
988
+ }
989
+
990
+
991
+ def _incomplete_receipt(
992
+ args: argparse.Namespace,
993
+ *,
994
+ common: Mapping[str, Any],
995
+ output_root: Path,
996
+ output_descriptor: int,
997
+ reason_code: str,
998
+ retained: Mapping[str, Any] | None,
999
+ receipt_installation: ReceiptInstallation,
1000
+ ) -> dict[str, Any] | None:
1001
+ """Install the typed-incomplete composite receipt one stopped publication leaves behind.
1002
+
1003
+ A publication that stops between two ranges has left every range before it durable and a
1004
+ typed-incomplete work journal beside them. This binds that journal's exact bytes, this output
1005
+ root, the ranges that are already immutable and the ordered per-invocation counter records the
1006
+ work got as far as -- which is precisely what ``--resume`` validates and replaces.
1007
+
1008
+ A publication that stopped before its first range committed anything has left nothing to
1009
+ continue, and gets no receipt: the refusal that stopped it is raised instead.
1010
+ """
1011
+
1012
+ found = _retained_journal(output_descriptor, logical_output_root=output_root)
1013
+ if found is None:
1014
+ return None
1015
+ raw, journal = found
1016
+ evidence = _journal_evidence(journal)
1017
+ if evidence is None:
1018
+ return None
1019
+ payload = compose_generation_receipt(
1020
+ {
1021
+ "schema_version": GENERATION_RECEIPT_SCHEMA,
1022
+ "status": PUBLISH_INCOMPLETE_STATUS,
1023
+ "reason_code": reason_code,
1024
+ "published_at": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
1025
+ "output_root_sha256": root_coordinate(output_root),
1026
+ **common,
1027
+ "publication": {"journal_sha256": sha256_bytes(raw), **evidence},
1028
+ # A generation that was not published has nothing to read back. The block is stated
1029
+ # rather than dropped, because a composite receipt states every one of its blocks.
1030
+ "readback": None,
1031
+ }
1032
+ )
1033
+ install_receipt(
1034
+ args.receipt_file,
1035
+ payload,
1036
+ maximum=MAX_COMPOSITE_RECEIPT_BYTES,
1037
+ label="receipt_file",
1038
+ code_prefix="PUBLISH",
1039
+ refusal=SourceContractError,
1040
+ replaces=retained,
1041
+ installation=receipt_installation,
1042
+ )
1043
+ return payload
1044
+
1045
+
1046
+ # ------------------------------------------------------------------------------------------------
1047
+ # The handoff: two receipts that have to describe one staging checkpoint
1048
+ # ------------------------------------------------------------------------------------------------
1049
+
1050
+
1051
+ def _verify_handoff(
1052
+ harvest: Mapping[str, Any],
1053
+ author: Mapping[str, Any],
1054
+ *,
1055
+ harvest_sha256: str,
1056
+ execution: Mapping[str, Any],
1057
+ ) -> None:
1058
+ if harvest.get("status") != "harvest_complete" or author.get("status") != "complete":
1059
+ raise SourceContractError(
1060
+ "PUBLISH_UPSTREAM_INCOMPLETE",
1061
+ "components",
1062
+ "a generation is published from a complete sweep and a complete authored generation",
1063
+ )
1064
+ if not is_digest(author.get("manifest_sha256")):
1065
+ raise SourceContractError(
1066
+ "PUBLISH_UPSTREAM_INCOMPLETE",
1067
+ "author_receipt.manifest_sha256",
1068
+ "the authored generation published no manifest to classify",
1069
+ )
1070
+ if author.get("harvest_receipt_sha256") != harvest_sha256:
1071
+ raise SourceContractError(
1072
+ "PUBLISH_STAGE_HANDOFF",
1073
+ "author_receipt.harvest_receipt_sha256",
1074
+ "the authoring receipt must bind the exact harvest receipt supplied for publication",
1075
+ )
1076
+ if harvest.get("execution") != execution or author.get("execution") != execution:
1077
+ raise SourceContractError(
1078
+ "PUBLISH_STAGE_HANDOFF",
1079
+ "components.execution",
1080
+ "harvest, author, and publisher must use one exact execution coordinate",
1081
+ )
1082
+ staging = author.get("staging")
1083
+ if not isinstance(staging, Mapping):
1084
+ raise SourceContractError(
1085
+ "PUBLISH_STAGE_HANDOFF",
1086
+ "author_receipt.staging",
1087
+ "an authoring receipt names the staging coordinate it authored",
1088
+ )
1089
+ for stated, retained, member in (
1090
+ (harvest.get("checkpoint_sha256"), staging.get("checkpoint_sha256"), "checkpoint_sha256"),
1091
+ (harvest.get("config_sha256"), staging.get("config_sha256"), "config_sha256"),
1092
+ (harvest.get("endpoint"), staging.get("endpoint"), "endpoint"),
1093
+ (
1094
+ harvest.get("harvester_coordinate"),
1095
+ staging.get("harvester_coordinate"),
1096
+ "harvester_coordinate",
1097
+ ),
1098
+ ):
1099
+ if stated is None or stated != retained:
1100
+ raise SourceContractError(
1101
+ "PUBLISH_STAGE_HANDOFF",
1102
+ f"author_receipt.staging.{member}",
1103
+ "the two stage receipts describe two different sweeps",
1104
+ )
1105
+ measures = harvest.get("measures")
1106
+ if not isinstance(measures, Mapping) or set(measures) != {
1107
+ "requests",
1108
+ "responses",
1109
+ "pages",
1110
+ "provider_records",
1111
+ }:
1112
+ raise SourceContractError(
1113
+ "PUBLISH_STAGE_HANDOFF",
1114
+ "harvest_receipt.measures",
1115
+ "a sweep states requests, responses, pages and provider records as four numbers",
1116
+ )
1117
+
1118
+
1119
+ def _author_manifest(root: Path, expected_sha256: str) -> tuple[bytes, dict[str, Any]]:
1120
+ raw, manifest = load_bounded_json(
1121
+ root / MANIFEST_FILENAME,
1122
+ label="author_root.manifest",
1123
+ maximum=MAX_AUTHORING_MANIFEST_BYTES,
1124
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1125
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1126
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1127
+ expected_sha256=expected_sha256,
1128
+ )
1129
+ if manifest.get("schema_version") != AUTHORING_MANIFEST_SCHEMA:
1130
+ raise SourceContractError(
1131
+ "PUBLISH_STAGE_HANDOFF",
1132
+ "author_root.manifest",
1133
+ "the authored root does not hold an authoring manifest",
1134
+ )
1135
+ return raw, manifest
1136
+
1137
+
1138
+ def _author_journal(root: Path, expected_sha256: str) -> tuple[bytes, dict[str, Any]]:
1139
+ """Read the work journal the authoring stage retained beside its shards, by exact digest.
1140
+
1141
+ The authoring stage publishes ``journal.json`` on every invocation and never removes it, so
1142
+ the digest an author receipt states is a claim about bytes that are still on disk. Reading it
1143
+ here is what turns that claim back into evidence.
1144
+ """
1145
+
1146
+ return load_bounded_json(
1147
+ root / JOURNAL_FILENAME,
1148
+ label="author_root.journal",
1149
+ maximum=MAX_AUTHORING_JOURNAL_BYTES,
1150
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1151
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1152
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1153
+ expected_sha256=expected_sha256,
1154
+ )
1155
+
1156
+
1157
+ def _verify_author_receipt(
1158
+ author: Mapping[str, Any],
1159
+ *,
1160
+ manifest: Mapping[str, Any],
1161
+ manifest_bytes: int,
1162
+ harvest: Mapping[str, Any],
1163
+ harvest_sha256: str,
1164
+ author_root: Path,
1165
+ execution: Mapping[str, Any],
1166
+ ) -> dict[str, Any]:
1167
+ """Re-derive an author receipt from its exact manifest and registered policy.
1168
+
1169
+ The receipt is carried into the authoritative composite, so none of its duplicated summaries
1170
+ may be accepted merely because the receipt bytes have a caller-supplied digest. This check is
1171
+ deliberately before the publication lock: a false receipt cannot create either output root.
1172
+
1173
+ What it returns is the generation evidence this command established for itself, which the
1174
+ composite states beside the carried receipt.
1175
+ """
1176
+
1177
+ if set(author) != _AUTHOR_RECEIPT_MEMBERS:
1178
+ raise SourceContractError(
1179
+ "PUBLISH_STAGE_HANDOFF",
1180
+ "author_receipt",
1181
+ "the authoring receipt must carry exactly its closed contract members",
1182
+ )
1183
+ if set(manifest) != _AUTHOR_MANIFEST_MEMBERS:
1184
+ raise SourceContractError(
1185
+ "PUBLISH_STAGE_HANDOFF",
1186
+ "author_root.manifest",
1187
+ "the authoring manifest must carry exactly its closed contract members",
1188
+ )
1189
+
1190
+ staging = manifest["staging"]
1191
+ coverage = manifest["coverage"]
1192
+ counts = manifest["counts"]
1193
+ layer_text_digests = manifest["layer_text_digests"]
1194
+ shards = manifest["shards"]
1195
+ limits = author["limits"]
1196
+ if not isinstance(staging, dict) or set(staging) != _AUTHOR_STAGING_MEMBERS:
1197
+ raise SourceContractError(
1198
+ "PUBLISH_STAGE_HANDOFF",
1199
+ "author_root.manifest.staging",
1200
+ "the authoring manifest must carry the complete staging coordinate",
1201
+ )
1202
+ if (
1203
+ not isinstance(counts, dict)
1204
+ or set(counts) != _AUTHOR_COUNT_MEMBERS
1205
+ or any(type(value) is not int or value < 0 for value in counts.values())
1206
+ ):
1207
+ raise SourceContractError(
1208
+ "PUBLISH_STAGE_HANDOFF",
1209
+ "author_root.manifest.counts",
1210
+ "the authoring manifest must carry its exact non-negative count contract",
1211
+ )
1212
+ if not coverage_is_valid(coverage):
1213
+ raise SourceContractError(
1214
+ "PUBLISH_STAGE_HANDOFF",
1215
+ "author_root.manifest.coverage",
1216
+ "the authoring manifest must state what it covers and what it drew from",
1217
+ )
1218
+ if (
1219
+ not isinstance(layer_text_digests, dict)
1220
+ or set(layer_text_digests) != set(EMBEDDING_LAYERS)
1221
+ or any(value is not None and not is_digest(value) for value in layer_text_digests.values())
1222
+ ):
1223
+ raise SourceContractError(
1224
+ "PUBLISH_STAGE_HANDOFF",
1225
+ "author_root.manifest.layer_text_digests",
1226
+ "the authoring manifest must bind every retrieval layer digest",
1227
+ )
1228
+ if not isinstance(shards, list) or len(shards) > MAX_RANGE_DESCRIPTORS:
1229
+ raise SourceContractError(
1230
+ "PUBLISH_STAGE_HANDOFF",
1231
+ "author_root.manifest.shards",
1232
+ "the authoring manifest shard population exceeds its fixed contract",
1233
+ )
1234
+ if (
1235
+ not isinstance(limits, dict)
1236
+ or set(limits) != _AUTHOR_LIMIT_MEMBERS
1237
+ or any(type(value) is not int or value < 1 for value in limits.values())
1238
+ or limits["max_shard_entries"] > MAX_AUTHORING_SHARD_ENTRIES
1239
+ or limits["max_shard_bytes"] > MAX_AUTHORING_SHARD_BYTES
1240
+ ):
1241
+ raise SourceContractError(
1242
+ "PUBLISH_STAGE_HANDOFF",
1243
+ "author_receipt.limits",
1244
+ "the authoring receipt must carry its exact bounded limit contract",
1245
+ )
1246
+
1247
+ digest_staging = _AUTHOR_STAGING_MEMBERS - {
1248
+ "endpoint",
1249
+ "harvester_coordinate",
1250
+ "pass_number",
1251
+ "unique_records",
1252
+ "raw_response_index_count",
1253
+ "normalized_page_index_count",
1254
+ "record_index_count",
1255
+ }
1256
+ count_staging = {
1257
+ "unique_records",
1258
+ "raw_response_index_count",
1259
+ "normalized_page_index_count",
1260
+ "record_index_count",
1261
+ }
1262
+ if (
1263
+ any(not is_digest(staging[name]) for name in digest_staging)
1264
+ or not isinstance(staging["endpoint"], str)
1265
+ or not staging["endpoint"]
1266
+ or not isinstance(staging["harvester_coordinate"], str)
1267
+ or not staging["harvester_coordinate"]
1268
+ or type(staging["pass_number"]) is not int
1269
+ or staging["pass_number"] < 1
1270
+ or any(type(staging[name]) is not int or staging[name] < 0 for name in count_staging)
1271
+ ):
1272
+ raise SourceContractError(
1273
+ "PUBLISH_STAGE_HANDOFF",
1274
+ "author_root.manifest.staging",
1275
+ "the authoring manifest staging coordinate has invalid members",
1276
+ )
1277
+
1278
+ manifest_body = {name: value for name, value in manifest.items() if name != "root_sha256"}
1279
+ if manifest["root_sha256"] != canonical_sha256(manifest_body):
1280
+ raise SourceContractError(
1281
+ "PUBLISH_STAGE_HANDOFF",
1282
+ "author_root.manifest.root_sha256",
1283
+ "the authoring manifest does not reproduce its own root coordinate",
1284
+ )
1285
+ try:
1286
+ policy = resolve_authoring_policy(manifest["policy_id"])
1287
+ except SourceContractError as error:
1288
+ raise SourceContractError(
1289
+ "PUBLISH_STAGE_HANDOFF",
1290
+ "author_root.manifest.policy_id",
1291
+ "the authoring manifest does not name a registered policy",
1292
+ ) from error
1293
+
1294
+ expected = {
1295
+ "schema_version": AUTHORING_RECEIPT_SCHEMA,
1296
+ "status": "complete",
1297
+ "reason_code": None,
1298
+ "policy_id": manifest["policy_id"],
1299
+ "policy_sha256": manifest["policy_sha256"],
1300
+ "provider_id": policy.provider_id,
1301
+ "harvester_coordinate": policy.harvester_coordinate,
1302
+ "staging": staging,
1303
+ "coverage": coverage,
1304
+ "counts": counts,
1305
+ "layer_text_digests": layer_text_digests,
1306
+ "shard_count": len(shards),
1307
+ "shards": shards,
1308
+ "manifest_sha256": author["manifest_sha256"],
1309
+ "harvest_receipt_sha256": harvest_sha256,
1310
+ "output_root_sha256": root_coordinate(author_root),
1311
+ "execution": execution,
1312
+ }
1313
+ # The two populations a coverage statement names are not free either. It draws from the
1314
+ # corpus this generation's own staging coordinate found, and it accounts for every record
1315
+ # this generation's own shards hold. `coverage_is_valid` above closed the statement against
1316
+ # itself; this closes it against the generation it describes.
1317
+ if (
1318
+ coverage["corpus_records"] != staging["unique_records"]
1319
+ or coverage["selected_records"] != counts["records"]
1320
+ ):
1321
+ raise SourceContractError(
1322
+ "PUBLISH_STAGE_HANDOFF",
1323
+ "author_root.manifest.coverage",
1324
+ "the stated coverage does not draw from this generation's corpus or does not "
1325
+ "account for every record it authored",
1326
+ )
1327
+ if manifest["policy_sha256"] != policy.digest:
1328
+ raise SourceContractError(
1329
+ "PUBLISH_STAGE_HANDOFF",
1330
+ "author_root.manifest.policy_sha256",
1331
+ "the authoring manifest policy fingerprint differs from its registered policy",
1332
+ )
1333
+ if staging["harvester_coordinate"] != policy.harvester_coordinate:
1334
+ raise SourceContractError(
1335
+ "PUBLISH_STAGE_HANDOFF",
1336
+ "author_root.manifest.staging.harvester_coordinate",
1337
+ "the staging coordinate differs from the registered authoring policy",
1338
+ )
1339
+ if (
1340
+ harvest.get("provider") != policy.provider_id
1341
+ or harvest.get("harvester_coordinate") != policy.harvester_coordinate
1342
+ ):
1343
+ raise SourceContractError(
1344
+ "PUBLISH_STAGE_HANDOFF",
1345
+ "harvest_receipt",
1346
+ "the harvest receipt differs from the registered authoring policy",
1347
+ )
1348
+ for member, value in expected.items():
1349
+ if author[member] != value:
1350
+ raise SourceContractError(
1351
+ "PUBLISH_STAGE_HANDOFF",
1352
+ f"author_receipt.{member}",
1353
+ "the authoring receipt differs from its exact manifest or resolved input",
1354
+ )
1355
+ return _verify_author_generation_evidence(
1356
+ author, manifest=manifest, manifest_bytes=manifest_bytes, author_root=author_root
1357
+ )
1358
+
1359
+
1360
+ def _verify_author_generation_evidence(
1361
+ author: Mapping[str, Any],
1362
+ *,
1363
+ manifest: Mapping[str, Any],
1364
+ manifest_bytes: int,
1365
+ author_root: Path,
1366
+ ) -> dict[str, Any]:
1367
+ """Bind the author receipt's journal, bounds and byte total to the generation it authored.
1368
+
1369
+ Everything above this point re-derives the receipt's *summaries* from the manifest. Three
1370
+ members are left over, and each one used to be believed on the strength of its own shape:
1371
+ the journal fingerprint, the bounds the invocation ran under, and the bytes it wrote.
1372
+
1373
+ Each is reconciled here against evidence the author root still holds. The journal digest is
1374
+ read back from the retained ``journal.json`` and the journal is required to describe exactly
1375
+ this manifest's generation. The bounds are required to admit the shards that were actually
1376
+ authored, which shows only that each was stated at least that wide. The byte total is an
1377
+ invocation-scoped measure -- a resumed authoring writes only what it added -- so it is
1378
+ reconciled against the interval the retained outputs allow: never less than the journal and
1379
+ manifest this invocation had to publish, and never more than the bytes that manifest and
1380
+ journal account for.
1381
+
1382
+ None of that reaches an exact value for six of the eight, so the returned evidence carries
1383
+ ``AUTHOR_CLAIM_RECONCILIATION`` rather than a list of the unreachable ones: naming only what
1384
+ could not be reached would leave a reader to conclude that everything unnamed was.
1385
+ """
1386
+
1387
+ if (
1388
+ not is_digest(author["journal_sha256"])
1389
+ or type(author["output_bytes"]) is not int
1390
+ or author["output_bytes"] < 0
1391
+ ):
1392
+ raise SourceContractError(
1393
+ "PUBLISH_STAGE_HANDOFF",
1394
+ "author_receipt",
1395
+ "the authoring receipt carries invalid generation evidence",
1396
+ )
1397
+ shards = manifest["shards"]
1398
+ for descriptor in shards:
1399
+ if not shard_descriptor_is_valid(descriptor):
1400
+ raise SourceContractError(
1401
+ "PUBLISH_STAGE_HANDOFF",
1402
+ "author_root.manifest.shards[]",
1403
+ "an authored shard descriptor must carry exactly its closed contract members",
1404
+ )
1405
+
1406
+ journal_raw, journal = _author_journal(author_root, author["journal_sha256"])
1407
+ if (
1408
+ set(journal) != _AUTHOR_JOURNAL_MEMBERS
1409
+ or journal["schema_version"] != AUTHORING_JOURNAL_SCHEMA
1410
+ ):
1411
+ raise SourceContractError(
1412
+ "PUBLISH_STAGE_HANDOFF",
1413
+ "author_root.journal",
1414
+ "the retained authoring journal must carry exactly its closed contract members",
1415
+ )
1416
+ if (
1417
+ journal["reason_code"] is not None
1418
+ or journal["policy_sha256"] != manifest["policy_sha256"]
1419
+ or journal["staging_checkpoint_sha256"] != manifest["staging"]["checkpoint_sha256"]
1420
+ or journal["coverage"] != manifest["coverage"]
1421
+ or journal["shards"] != shards
1422
+ or journal["counts"] != manifest["counts"]
1423
+ or journal["layer_text_digests"] != manifest["layer_text_digests"]
1424
+ or journal["last_provider_record_id"]
1425
+ != (shards[-1]["last_provider_record_id"] if shards else None)
1426
+ ):
1427
+ raise SourceContractError(
1428
+ "PUBLISH_STAGE_HANDOFF",
1429
+ "author_root.journal",
1430
+ "the retained authoring journal describes another authored generation",
1431
+ )
1432
+
1433
+ limits = author["limits"]
1434
+ if (
1435
+ limits["max_records"] < manifest["counts"]["records"]
1436
+ or any(limits["max_shard_entries"] < descriptor["item_count"] for descriptor in shards)
1437
+ or any(limits["max_shard_bytes"] < descriptor["bytes"] for descriptor in shards)
1438
+ ):
1439
+ raise SourceContractError(
1440
+ "PUBLISH_STAGE_HANDOFF",
1441
+ "author_receipt.limits",
1442
+ "the authoring receipt states bounds the generation it authored exceeds",
1443
+ )
1444
+
1445
+ # One shard is one immutable digest-named member, so a repeated digest is one file.
1446
+ shard_bytes = sum({descriptor["sha256"]: descriptor["bytes"] for descriptor in shards}.values())
1447
+ published_bytes = len(journal_raw) + manifest_bytes
1448
+ accounted_root_bytes = shard_bytes + published_bytes
1449
+ if not published_bytes <= author["output_bytes"] <= accounted_root_bytes:
1450
+ raise SourceContractError(
1451
+ "PUBLISH_STAGE_HANDOFF",
1452
+ "author_receipt.output_bytes",
1453
+ "the authoring receipt states a byte total the authored root cannot account for",
1454
+ )
1455
+ return {
1456
+ "journal_sha256": author["journal_sha256"],
1457
+ "accounted_root_bytes": accounted_root_bytes,
1458
+ "claim_reconciliation": dict(AUTHOR_CLAIM_RECONCILIATION),
1459
+ }
1460
+
1461
+
1462
+ # ------------------------------------------------------------------------------------------------
1463
+ # Predecessor coordinates
1464
+ # ------------------------------------------------------------------------------------------------
1465
+
1466
+
1467
+ def _predecessor_coordinates(args: argparse.Namespace) -> dict[str, Any] | None:
1468
+ stated = [name for name in _PREDECESSOR_ARGUMENTS if getattr(args, name) is not None]
1469
+ if args.predecessor_mode == "none":
1470
+ if stated:
1471
+ raise SourceContractError(
1472
+ "PUBLISH_PREDECESSOR_COORDINATE",
1473
+ stated[0],
1474
+ "a generation with no predecessor takes no predecessor argument at all",
1475
+ )
1476
+ return None
1477
+ if len(stated) != len(_PREDECESSOR_ARGUMENTS):
1478
+ missing = [name for name in _PREDECESSOR_ARGUMENTS if getattr(args, name) is None]
1479
+ raise SourceContractError(
1480
+ "PUBLISH_PREDECESSOR_COORDINATE",
1481
+ missing[0],
1482
+ "an exact predecessor is named by all six of its roots and digests",
1483
+ )
1484
+ for name in (
1485
+ "predecessor_identity_digest",
1486
+ "predecessor_history_digest",
1487
+ "predecessor_catalog_digest",
1488
+ ):
1489
+ if not is_digest(getattr(args, name)):
1490
+ raise SourceContractError(
1491
+ "PUBLISH_PREDECESSOR_COORDINATE", name, "must be a lowercase SHA-256"
1492
+ )
1493
+ return {
1494
+ "identity_root": Path(args.predecessor_identity_root),
1495
+ "identity_digest": args.predecessor_identity_digest,
1496
+ "history_root": Path(args.predecessor_history_root),
1497
+ "history_digest": args.predecessor_history_digest,
1498
+ "catalog_root": Path(args.predecessor_catalog_root),
1499
+ "catalog_digest": args.predecessor_catalog_digest,
1500
+ }
1501
+
1502
+
1503
+ def _read_predecessor_heads(
1504
+ predecessor: Mapping[str, Any] | None, *, identity_descriptor: int | None = None
1505
+ ) -> dict[str, Any] | None:
1506
+ """Read all three predecessor heads under the fixed caps, and cross-check what they claim."""
1507
+
1508
+ if predecessor is None:
1509
+ return None
1510
+ # The identity generation is opened through the exact manifest name its writer owns.
1511
+ if identity_descriptor is None:
1512
+ _raw, identity = load_bounded_json(
1513
+ predecessor["identity_root"] / IDENTITY_MANIFEST_FILENAME,
1514
+ label="predecessor_identity_root.manifest",
1515
+ maximum=MAX_PREDECESSOR_HEAD_BYTES,
1516
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1517
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1518
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1519
+ expected_sha256=predecessor["identity_digest"],
1520
+ )
1521
+ else:
1522
+ _raw, identity = load_bounded_json_at(
1523
+ identity_descriptor,
1524
+ IDENTITY_MANIFEST_FILENAME,
1525
+ label="predecessor_identity_root.manifest",
1526
+ maximum=MAX_PREDECESSOR_HEAD_BYTES,
1527
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1528
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1529
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1530
+ )
1531
+ if sha256_bytes(_raw) != predecessor["identity_digest"]:
1532
+ raise SourceContractError(
1533
+ "GENERATION_INPUT_DIGEST",
1534
+ "predecessor_identity_root.manifest",
1535
+ "the retained identity manifest differs from its required digest",
1536
+ )
1537
+ if identity.get("schema_version") != IDENTITY_MANIFEST_SCHEMA:
1538
+ raise SourceContractError(
1539
+ "PUBLISH_PREDECESSOR_COORDINATE",
1540
+ "predecessor_identity_root.manifest",
1541
+ "the predecessor identity root does not hold an identity generation",
1542
+ )
1543
+ identity_records = identity.get("record_count")
1544
+ if type(identity_records) is not int or identity_records < 0:
1545
+ raise SourceContractError(
1546
+ "PUBLISH_PREDECESSOR_COORDINATE",
1547
+ "predecessor_identity_root.manifest",
1548
+ "an identity generation states how many records it carries",
1549
+ )
1550
+ _raw, history = load_bounded_json(
1551
+ predecessor["history_root"] / DELTA_MANIFEST_FILENAME,
1552
+ label="predecessor_history_root.manifest",
1553
+ maximum=min(MAX_PREDECESSOR_HEAD_BYTES, MAX_DELTA_MANIFEST_BYTES),
1554
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1555
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1556
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1557
+ expected_sha256=predecessor["history_digest"],
1558
+ )
1559
+ if history.get("outputs", {}).get("identity_manifest_sha256") != predecessor["identity_digest"]:
1560
+ raise SourceContractError(
1561
+ "PUBLISH_PREDECESSOR_COORDINATE",
1562
+ "predecessor_history_root",
1563
+ "the predecessor's classification does not name the identity generation given here",
1564
+ )
1565
+ _raw, head = load_bounded_json(
1566
+ predecessor["catalog_root"] / "packed-head.json",
1567
+ label="predecessor_catalog_root.head",
1568
+ maximum=MAX_PREDECESSOR_HEAD_BYTES,
1569
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1570
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1571
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1572
+ expected_sha256=predecessor["catalog_digest"],
1573
+ )
1574
+ if head.get("generation_sha256") != predecessor["history_digest"]:
1575
+ raise SourceContractError(
1576
+ "PUBLISH_PREDECESSOR_COORDINATE",
1577
+ "predecessor_catalog_root.head.generation_sha256",
1578
+ "the predecessor packed catalog was not published from the named history generation",
1579
+ )
1580
+ ranges = head.get("ranges", ())
1581
+ if not isinstance(ranges, list) or len(ranges) > MAX_PREDECESSOR_RANGE_DESCRIPTORS:
1582
+ raise SourceContractError(
1583
+ "PUBLISH_PREDECESSOR_COORDINATE",
1584
+ "predecessor_catalog_root.head.ranges",
1585
+ f"a predecessor head states at most {MAX_PREDECESSOR_RANGE_DESCRIPTORS} ranges",
1586
+ )
1587
+ return {
1588
+ "identity_manifest_sha256": predecessor["identity_digest"],
1589
+ "identity_root_sha256": root_coordinate(predecessor["identity_root"]),
1590
+ "history_manifest_sha256": predecessor["history_digest"],
1591
+ "history_root_sha256": root_coordinate(predecessor["history_root"]),
1592
+ "catalog_head_sha256": predecessor["catalog_digest"],
1593
+ "catalog_root_sha256": root_coordinate(predecessor["catalog_root"]),
1594
+ "catalog_range_count": len(ranges),
1595
+ # The predecessor record count comes from the identity manifest's required member.
1596
+ "identity_record_count": identity_records,
1597
+ }
1598
+
1599
+
1600
+ # ------------------------------------------------------------------------------------------------
1601
+ # Classification and publication
1602
+ # ------------------------------------------------------------------------------------------------
1603
+
1604
+
1605
+ def _classify(
1606
+ args: argparse.Namespace,
1607
+ *,
1608
+ author: Mapping[str, Any],
1609
+ harvest: Mapping[str, Any],
1610
+ delta_root: Path,
1611
+ output_descriptor: int,
1612
+ coordinate_root: Path | None = None,
1613
+ predecessor: Mapping[str, Any] | None,
1614
+ resumed: bool,
1615
+ predecessor_identity_descriptor: int | None = None,
1616
+ ) -> dict[str, Any]:
1617
+ with retained_child_directory(
1618
+ output_descriptor,
1619
+ DELTA_DIRNAME,
1620
+ create=not resumed,
1621
+ label="delta.output",
1622
+ code_prefix="PUBLISH",
1623
+ refusal=SourceContractError,
1624
+ ) as delta_descriptor:
1625
+ if resumed:
1626
+ try:
1627
+ raw, existing = load_bounded_json_at(
1628
+ delta_descriptor,
1629
+ DELTA_MANIFEST_FILENAME,
1630
+ label="delta.manifest",
1631
+ maximum=MAX_DELTA_MANIFEST_BYTES,
1632
+ max_depth=MAX_PREDECESSOR_HEAD_DEPTH,
1633
+ max_members=MAX_STAGE_RECEIPT_MEMBERS,
1634
+ max_items=MAX_PREDECESSOR_RANGE_DESCRIPTORS,
1635
+ )
1636
+ except SourceContractError as error:
1637
+ if error.code == "GENERATION_INPUT_PATH":
1638
+ raise SourceContractError(
1639
+ "PUBLISH_RESUME_RECEIPT",
1640
+ "classification",
1641
+ "the interrupted publication must retain its exact classification manifest",
1642
+ ) from error
1643
+ raise
1644
+ return {
1645
+ "manifest_sha256": sha256_bytes(raw),
1646
+ "identity_manifest_sha256": existing["outputs"]["identity_manifest_sha256"],
1647
+ "counts": {name: existing["counts"][name] for name in DELTA_CLASSIFICATIONS},
1648
+ "reuse": existing["reuse"],
1649
+ "measures": existing["measures"],
1650
+ "shard_count": existing["outputs"]["shard_count"],
1651
+ "classification_digests": existing["outputs"]["classification_digests"],
1652
+ "identity_root_sha256": root_coordinate(
1653
+ (coordinate_root or delta_root) / DELTA_IDENTITY_DIRNAME
1654
+ ),
1655
+ "history_root_sha256": root_coordinate(coordinate_root or delta_root),
1656
+ }
1657
+ measures = harvest["measures"]
1658
+ result = run_streaming_delta(
1659
+ authoring_root=Path(args.author_root),
1660
+ expected_authoring_manifest_sha256=author["manifest_sha256"],
1661
+ output_root=delta_root,
1662
+ output_descriptor=delta_descriptor,
1663
+ measures=SweepMeasures(
1664
+ requests=measures["requests"],
1665
+ responses=measures["responses"],
1666
+ pages=measures["pages"],
1667
+ provider_records=measures["provider_records"],
1668
+ ),
1669
+ observed_at=_observed_at(harvest),
1670
+ limits=StreamingDeltaLimits(
1671
+ max_records=args.max_ranges * args.max_range_entries,
1672
+ max_disk_bytes=args.max_disk_bytes,
1673
+ max_wall_seconds=args.max_wall_seconds,
1674
+ ),
1675
+ predecessor_root=None if predecessor is None else predecessor["identity_root"],
1676
+ expected_predecessor_manifest_sha256=(
1677
+ None if predecessor is None else predecessor["identity_digest"]
1678
+ ),
1679
+ predecessor_root_descriptor=predecessor_identity_descriptor,
1680
+ )
1681
+ receipt = result.receipt
1682
+ return {
1683
+ "manifest_sha256": result.manifest_sha256,
1684
+ "identity_manifest_sha256": result.identity_manifest_sha256,
1685
+ "counts": result.counts,
1686
+ "reuse": receipt["reuse"],
1687
+ "measures": receipt["measures"],
1688
+ "shard_count": receipt["outputs"]["shard_count"],
1689
+ "classification_digests": receipt["outputs"]["classification_digests"],
1690
+ "identity_root_sha256": root_coordinate(
1691
+ (coordinate_root or delta_root) / DELTA_IDENTITY_DIRNAME
1692
+ ),
1693
+ "history_root_sha256": root_coordinate(coordinate_root or delta_root),
1694
+ }
1695
+
1696
+
1697
+ def _observed_at(harvest: Mapping[str, Any]) -> str:
1698
+ observed = harvest.get("observed_at")
1699
+ if isinstance(observed, str) and observed:
1700
+ return observed
1701
+ return datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
1702
+
1703
+
1704
+ def _backends(args: argparse.Namespace) -> list[Any]:
1705
+ backends: list[Any] = [LexicalHashingBackend()]
1706
+ if args.neural_backend is None:
1707
+ if args.neural_pin is not None or args.model_root is not None:
1708
+ raise SourceContractError(
1709
+ "PUBLISH_BACKEND_COORDINATE",
1710
+ "neural_backend",
1711
+ "a pinned model root belongs to the neural backend that reads it",
1712
+ )
1713
+ return backends
1714
+ if args.model_root is None or not is_digest(args.neural_pin):
1715
+ raise SourceContractError(
1716
+ "PUBLISH_BACKEND_COORDINATE",
1717
+ "neural_backend",
1718
+ "a neural representation names its governed model root and its exact pin",
1719
+ )
1720
+ from mostlyright.data_harness.sources.catalog.neural import resolve_local_neural_backend
1721
+
1722
+ backends.append(resolve_local_neural_backend(args.model_root))
1723
+ return backends
1724
+
1725
+
1726
+ def _publication_evidence(
1727
+ receipt: Mapping[str, Any], publication: Any, backends: Sequence[Any]
1728
+ ) -> dict[str, Any]:
1729
+ del backends
1730
+ outputs = receipt["outputs"]
1731
+ return {
1732
+ # The publication receipt is carried whole, not summarised. A packed search authenticates
1733
+ # its store against exactly this document, so a reader that had to rebuild it from the
1734
+ # block below would be authenticating against a copy somebody made.
1735
+ "packed_receipt": dict(receipt),
1736
+ "coordinate_sha256": receipt["coordinate_sha256"],
1737
+ "expected_head_sha256": publication.head_sha256,
1738
+ "journal_sha256": publication.journal_sha256,
1739
+ "backends": list(receipt["backends"]),
1740
+ "posting_backend_coordinate": receipt["posting_backend_coordinate"],
1741
+ "adapter_mode": receipt["adapter_mode"],
1742
+ "predecessor": receipt["inputs"]["predecessor_head_sha256"],
1743
+ "counts": dict(publication.counts),
1744
+ "counters": dict(publication.counters),
1745
+ "counter_records": [dict(record) for record in receipt["invocations"]],
1746
+ "range_manifest_sha256s": [
1747
+ descriptor["manifest_sha256"] for descriptor in outputs["ranges"]
1748
+ ],
1749
+ "ranges_chain_sha256": outputs["ranges_chain_sha256"],
1750
+ "vector_payload_sha256s": list(outputs["vector_member_sha256s"]),
1751
+ "posting_member_sha256s": list(outputs["posting_member_sha256s"]),
1752
+ "bound_member_sha256s": list(outputs["bound_member_sha256s"]),
1753
+ "residency": receipt["residency"],
1754
+ }
1755
+
1756
+
1757
+ # ------------------------------------------------------------------------------------------------
1758
+ # Bounds
1759
+ # ------------------------------------------------------------------------------------------------
1760
+
1761
+
1762
+ def _validate_limits(args: argparse.Namespace) -> None:
1763
+ for name, exact in _FIXED.items():
1764
+ value = getattr(args, name)
1765
+ if type(value) is not int or value != exact:
1766
+ raise SourceContractError(
1767
+ "PUBLISH_LIMIT",
1768
+ name,
1769
+ f"this bound is fixed at {exact} and is not caller-widenable",
1770
+ )
1771
+ for name in (
1772
+ "max_batch_invocations",
1773
+ "max_encoded_layer_items",
1774
+ "max_backend_scalar_invocations",
1775
+ "max_encoded_input_bytes",
1776
+ "max_disk_bytes",
1777
+ "max_wall_seconds",
1778
+ ):
1779
+ value = getattr(args, name)
1780
+ if type(value) is not int or value < 1:
1781
+ raise SourceContractError(
1782
+ "PUBLISH_LIMIT", name, "must be a positive canonical decimal integer"
1783
+ )
1784
+
1785
+
1786
+ def _verify_work_budget(args: argparse.Namespace, receipt: Mapping[str, Any]) -> None:
1787
+ """The three work counters and the encoded input bytes, checked as totals and as sums.
1788
+
1789
+ The publisher owns these numbers -- it produces them from seam-minted records rather than
1790
+ accepting them -- so the caller's budget is checked against what was actually done, and against
1791
+ the ordered per-invocation records the composite receipt is about to state.
1792
+ """
1793
+
1794
+ counters = receipt["counters"]
1795
+ for counter, cap in (
1796
+ ("batch_invocations", args.max_batch_invocations),
1797
+ ("encoded_layer_items", args.max_encoded_layer_items),
1798
+ ("backend_scalar_invocations", args.max_backend_scalar_invocations),
1799
+ ("encoded_utf8_bytes", args.max_encoded_input_bytes),
1800
+ ):
1801
+ if counters[counter] > cap:
1802
+ raise SourceContractError(
1803
+ "PUBLISH_WORK_LIMIT",
1804
+ f"counters.{counter}",
1805
+ "the publication did more of this work than the caller allowed it to",
1806
+ )
1807
+ stated = sum(record[counter] for record in receipt["invocations"])
1808
+ if stated != counters[counter]:
1809
+ raise SourceContractError(
1810
+ "PUBLISH_WORK_LIMIT",
1811
+ f"counters.{counter}",
1812
+ "the ordered per-invocation records do not sum to the stated total",
1813
+ )
1814
+
1815
+
1816
+ def _limits_evidence(args: argparse.Namespace) -> dict[str, int]:
1817
+ return {
1818
+ **{name: getattr(args, name) for name in _FIXED},
1819
+ "max_batch_invocations": args.max_batch_invocations,
1820
+ "max_encoded_layer_items": args.max_encoded_layer_items,
1821
+ "max_backend_scalar_invocations": args.max_backend_scalar_invocations,
1822
+ "max_encoded_input_bytes": args.max_encoded_input_bytes,
1823
+ "max_disk_bytes": args.max_disk_bytes,
1824
+ "max_wall_seconds": args.max_wall_seconds,
1825
+ }
1826
+
1827
+
1828
+ def _exit_code(payload: Mapping[str, Any]) -> int:
1829
+ return 0 if payload.get("status") == PUBLISH_COMPLETE_STATUS else 1
1830
+
1831
+
1832
+ SPEC = CommandSpec(
1833
+ name="catalog-publish",
1834
+ help="classify one authored generation, publish it, and write its one composite receipt",
1835
+ add_arguments=add_arguments,
1836
+ run=run,
1837
+ exit_code=_exit_code,
1838
+ )