mostlyright-data 0.23.0__tar.gz → 0.25.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/PKG-INFO +31 -12
  2. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/README.md +30 -11
  3. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/pyproject.toml +1 -1
  4. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/SKILL.md +39 -2
  5. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +88 -0
  6. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +18 -8
  7. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/commands.md +20 -2
  8. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/live-run.md +8 -0
  9. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/promote.md +13 -7
  10. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/reference-pages.md +3 -2
  11. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/sources.md +20 -13
  12. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/page_coverage.py +4 -4
  13. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/recipe.py +468 -8
  14. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/router.py +5 -1
  15. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/stream_venue.py +199 -8
  16. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4.py +55 -6
  17. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_runs.py +160 -16
  18. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_tables.py +573 -14
  19. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/render.py +26 -0
  20. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/.gitignore +0 -0
  21. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/scripts/hatch_build.py +0 -0
  22. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/agents/openai.yaml +0 -0
  23. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
  24. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
  25. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
  26. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
  27. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
  28. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
  29. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
  30. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/agent-protocol.md +0 -0
  31. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
  32. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
  33. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/boundaries.md +0 -0
  34. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
  35. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
  36. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/installation-parity.md +0 -0
  37. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/narrating-the-run.md +0 -0
  38. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
  39. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/one-install.md +0 -0
  40. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/prediction-labels.md +0 -0
  41. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/readers.md +0 -0
  42. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/receipts.md +0 -0
  43. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
  44. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
  45. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/required-protocol.md +0 -0
  46. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/source-credentials.md +0 -0
  47. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
  48. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/transforms.md +0 -0
  49. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/user-communication-contract.md +0 -0
  50. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
  51. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
  52. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/__init__.py +0 -0
  53. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/agent_protocol.py +0 -0
  54. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/canonical.py +0 -0
  55. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/formats.py +0 -0
  56. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
  57. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/key_seam.py +0 -0
  58. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
  59. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/session_probes.py +0 -0
  60. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/skill_assets.py +0 -0
  61. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/table_manifest.py +0 -0
  62. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/__init__.py +0 -0
  63. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/acquire.py +0 -0
  64. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
  65. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/activity.py +0 -0
  66. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/approvals.py +0 -0
  67. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/categories.py +0 -0
  68. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/commands.py +0 -0
  69. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
  70. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/download.py +0 -0
  71. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/narrative.py +0 -0
  72. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/parity.py +0 -0
  73. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/probe.py +0 -0
  74. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
  75. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/propose.py +0 -0
  76. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
  77. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
  78. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/research.py +0 -0
  79. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/runs.py +0 -0
  80. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/session.py +0 -0
  81. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/stream.py +0 -0
  82. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/transport.py +0 -0
  83. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
  84. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
  85. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
  86. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
  87. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
  88. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_datasets.py +0 -0
  89. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
  90. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
  91. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
  92. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
  93. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
  94. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
  95. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
  96. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/__init__.py +0 -0
  97. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/attendance.py +0 -0
  98. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/clarification.py +0 -0
  99. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
  100. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
  101. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
  102. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
  103. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
  104. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
  105. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
  106. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
  107. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/credentials.py +0 -0
  108. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/login.py +0 -0
  109. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
  110. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
  111. {mostlyright_data-0.23.0 → mostlyright_data-0.25.0}/src/mostlyright/data_harness/ux/remediation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mostlyright-data
3
- Version: 0.23.0
3
+ Version: 0.25.0
4
4
  Summary: Mostly Right hosted CLI for reviewed datasets
5
5
  Project-URL: Homepage, https://mostlyright.md/
6
6
  Project-URL: Documentation, https://mostlyright.md/docs/guides/cli/
@@ -89,6 +89,17 @@ can reissue supported platform execution failures without changing the recipe.
89
89
 
90
90
  Use the identifiers returned by registration and run submission. `mr-data dataset create`
91
91
  creates a dataset page before a recipe is ready. `mr-data watch RUN_ID` follows a submitted run.
92
+ A scheduled refresh runs only Studio's persisted bounded source-action plan; it never silently
93
+ falls back to a mutable whole-source fetch. `mr-data table resync TABLE_ID --request-id UUID`
94
+ explicitly requests a full source reread when that is needed. Choose and retain the UUID before
95
+ submitting: if the response is lost, repeat that exact command with the same UUID rather than
96
+ starting another reread. A persisted spend hold is still an accepted resync: its receipt names
97
+ the run, projection and exact `mr-data run --confirm-held RUN_ID` action. If it is a sample-first
98
+ preview, confirm that preview first, then release its linked full
99
+ only after the preview succeeds. `mr-data recipe readiness` reads Studio's paginated,
100
+ immutable-revision inventory before a production-wide schedule sweep: predecessor,
101
+ incremental-materialization and differential-proof readiness, together with each source's next
102
+ action. It reports Studio's persisted facts; it does not authorize a schedule.
92
103
 
93
104
  To request an offline replay of retained inputs with a registered revision:
94
105
 
@@ -100,16 +111,23 @@ Studio must have replay enabled and the named successful run must belong to the
100
111
  its raw inputs still retained. Replay compares against that run and never becomes the live version.
101
112
  Studio returns a typed refusal when replay is unavailable; the CLI does not fetch sources locally.
102
113
 
103
- The hosted engine executes sample, full and refresh runs. A refresh uses each source's declared
104
- update behavior: bounded source windows merge with retained history; snapshot sources are
105
- revalidated or acquired again. The table's transformations and checks run over the resulting
106
- source relations. URL windows may use declared query parameters or path placeholders; a fixed
107
- date URL does not advance automatically. Configure a correction lookback where the publisher
108
- can revise earlier observations.
109
-
110
- A source exposing only its current snapshot cannot supply a historical delta. It remains a
111
- supported source, with snapshot comparison and recomputation rather than a claim that only new
112
- rows were fetched. Recorded streams use their registered capture and continuation semantics.
114
+ The hosted engine executes sample, full and refresh runs. Studio's action classifier can name
115
+ closed-source reuse, request-window or collection acquisition, and recorded stream input, but
116
+ normal refresh admits only two families of plan: every source a recorded stream, and at least one
117
+ direct partition request window with a compatible predecessor, with `closed` reuse permitted
118
+ beside it. Once such a plan holds more than one source, every window in it must agree on one
119
+ `merge.materialization`, `merge.partition.column` and `merge.partition.key`, and that column must
120
+ be one the table declares. Closed-only plans, any plan carrying a collection, a window beside a
121
+ recorded stream, a mutable snapshot and any other unwindowed source return `RESYNC_REQUIRED`
122
+ before acquisition. Nothing is revalidated or re-acquired whole as an implicit fallback. Use
123
+ `mr-data table resync TABLE_ID --request-id UUID` when a deliberate full source reread is
124
+ intended, retaining that UUID through an uncertain response. URL windows may use declared query
125
+ parameters or path placeholders; a fixed date URL does not advance automatically.
126
+ Configure a correction lookback where the publisher can revise earlier observations.
127
+
128
+ A source exposing only its current snapshot cannot supply a historical delta. It is supported only
129
+ through an explicit table resync; refresh does not turn its whole current result into an
130
+ incremental update. Recorded streams use their registered capture and continuation semantics.
113
131
 
114
132
  See [Recipe documents](docs/RECIPE-DOCUMENT.md) for source-window and bootstrap contracts.
115
133
  A table's first succeeded run goes live on its own, whatever mode it was; `mr-data promote
@@ -135,7 +153,8 @@ worker executables or image publisher.
135
153
  - [Recipe examples](https://mostlyright.md/docs/recipes/)
136
154
  - [Join market settlements](https://mostlyright.md/docs/recipes/market-settlement-join/)
137
155
  - [Compare a forecast with observations](https://mostlyright.md/docs/recipes/forecast-vs-observation/)
138
- - [Replace a snapshot on refresh](https://mostlyright.md/docs/recipes/snapshot-window/)
156
+ - [Model a snapshot source](https://mostlyright.md/docs/recipes/snapshot-window/) — normal refresh
157
+ is unavailable until its bounded materializer ships; use explicit table resync today
139
158
  - [Union many weather stations](https://mostlyright.md/docs/recipes/many-station-weather/)
140
159
  - [Aggregate a stream into bars](https://mostlyright.md/docs/recipes/stream-to-bars/)
141
160
  - [Use a public dataset](https://mostlyright.md/docs/guides/use-public-datasets/)
@@ -77,6 +77,17 @@ can reissue supported platform execution failures without changing the recipe.
77
77
 
78
78
  Use the identifiers returned by registration and run submission. `mr-data dataset create`
79
79
  creates a dataset page before a recipe is ready. `mr-data watch RUN_ID` follows a submitted run.
80
+ A scheduled refresh runs only Studio's persisted bounded source-action plan; it never silently
81
+ falls back to a mutable whole-source fetch. `mr-data table resync TABLE_ID --request-id UUID`
82
+ explicitly requests a full source reread when that is needed. Choose and retain the UUID before
83
+ submitting: if the response is lost, repeat that exact command with the same UUID rather than
84
+ starting another reread. A persisted spend hold is still an accepted resync: its receipt names
85
+ the run, projection and exact `mr-data run --confirm-held RUN_ID` action. If it is a sample-first
86
+ preview, confirm that preview first, then release its linked full
87
+ only after the preview succeeds. `mr-data recipe readiness` reads Studio's paginated,
88
+ immutable-revision inventory before a production-wide schedule sweep: predecessor,
89
+ incremental-materialization and differential-proof readiness, together with each source's next
90
+ action. It reports Studio's persisted facts; it does not authorize a schedule.
80
91
 
81
92
  To request an offline replay of retained inputs with a registered revision:
82
93
 
@@ -88,16 +99,23 @@ Studio must have replay enabled and the named successful run must belong to the
88
99
  its raw inputs still retained. Replay compares against that run and never becomes the live version.
89
100
  Studio returns a typed refusal when replay is unavailable; the CLI does not fetch sources locally.
90
101
 
91
- The hosted engine executes sample, full and refresh runs. A refresh uses each source's declared
92
- update behavior: bounded source windows merge with retained history; snapshot sources are
93
- revalidated or acquired again. The table's transformations and checks run over the resulting
94
- source relations. URL windows may use declared query parameters or path placeholders; a fixed
95
- date URL does not advance automatically. Configure a correction lookback where the publisher
96
- can revise earlier observations.
97
-
98
- A source exposing only its current snapshot cannot supply a historical delta. It remains a
99
- supported source, with snapshot comparison and recomputation rather than a claim that only new
100
- rows were fetched. Recorded streams use their registered capture and continuation semantics.
102
+ The hosted engine executes sample, full and refresh runs. Studio's action classifier can name
103
+ closed-source reuse, request-window or collection acquisition, and recorded stream input, but
104
+ normal refresh admits only two families of plan: every source a recorded stream, and at least one
105
+ direct partition request window with a compatible predecessor, with `closed` reuse permitted
106
+ beside it. Once such a plan holds more than one source, every window in it must agree on one
107
+ `merge.materialization`, `merge.partition.column` and `merge.partition.key`, and that column must
108
+ be one the table declares. Closed-only plans, any plan carrying a collection, a window beside a
109
+ recorded stream, a mutable snapshot and any other unwindowed source return `RESYNC_REQUIRED`
110
+ before acquisition. Nothing is revalidated or re-acquired whole as an implicit fallback. Use
111
+ `mr-data table resync TABLE_ID --request-id UUID` when a deliberate full source reread is
112
+ intended, retaining that UUID through an uncertain response. URL windows may use declared query
113
+ parameters or path placeholders; a fixed date URL does not advance automatically.
114
+ Configure a correction lookback where the publisher can revise earlier observations.
115
+
116
+ A source exposing only its current snapshot cannot supply a historical delta. It is supported only
117
+ through an explicit table resync; refresh does not turn its whole current result into an
118
+ incremental update. Recorded streams use their registered capture and continuation semantics.
101
119
 
102
120
  See [Recipe documents](docs/RECIPE-DOCUMENT.md) for source-window and bootstrap contracts.
103
121
  A table's first succeeded run goes live on its own, whatever mode it was; `mr-data promote
@@ -123,7 +141,8 @@ worker executables or image publisher.
123
141
  - [Recipe examples](https://mostlyright.md/docs/recipes/)
124
142
  - [Join market settlements](https://mostlyright.md/docs/recipes/market-settlement-join/)
125
143
  - [Compare a forecast with observations](https://mostlyright.md/docs/recipes/forecast-vs-observation/)
126
- - [Replace a snapshot on refresh](https://mostlyright.md/docs/recipes/snapshot-window/)
144
+ - [Model a snapshot source](https://mostlyright.md/docs/recipes/snapshot-window/) — normal refresh
145
+ is unavailable until its bounded materializer ships; use explicit table resync today
127
146
  - [Union many weather stations](https://mostlyright.md/docs/recipes/many-station-weather/)
128
147
  - [Aggregate a stream into bars](https://mostlyright.md/docs/recipes/stream-to-bars/)
129
148
  - [Use a public dataset](https://mostlyright.md/docs/guides/use-public-datasets/)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mostlyright-data"
3
- version = "0.23.0"
3
+ version = "0.25.0"
4
4
  description = "Mostly Right hosted CLI for reviewed datasets"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -37,6 +37,24 @@ references. Load the reference for the current stage only, not the entire librar
37
37
  Bind `dataset.id`, describe columns, declare checks and units, and register with
38
38
  `mr-data recipe RECIPE.json --json`. Repair all lint findings before queuing work.
39
39
  The server returns the recipe digest; do not compute or invent it.
40
+ Decide each source's refresh continuation before you register it: `closed: true` where the
41
+ address names a range that has demonstrably ended, a `window` with **both** `request`
42
+ endpoints where the address carries a date range, a `collection` where the corpus is an index
43
+ of pages, and nothing where the source is mutable with no changed-since contract. A source
44
+ declaring none of these is `resync_only`, and one `resync_only` source makes the entire table
45
+ resync-only: it will never refresh on a schedule. Declaring every source is only the first
46
+ gate; the shape those declarations form is the second, and Studio admits two families only:
47
+ every source a recorded stream, or at least one `window` with a `request`, with `closed`
48
+ siblings permitted beside it. A closed-only plan, any plan carrying a `collection`, and a
49
+ `window` beside a recorded stream are each refused, so `closed: true` alone does not make a
50
+ table refresh. The second family is necessary and not sufficient: as soon as the plan holds
51
+ more than one source, every window in it must agree on one `merge.materialization`,
52
+ `merge.partition.column` and `merge.partition.key`, and that column must be a name
53
+ `table.columns` declares. Adding one `closed` sibling beside a lone window is what turns that
54
+ requirement on, so a window partitioning on the publisher's own header under a name the table
55
+ renames refreshes alone and stops the moment a second source joins it. Declare only what the
56
+ source's own address proves, and name the `resync_only` sources and the reason in the build
57
+ message.
40
58
  6. Read [build sizing](references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md).
41
59
  Choose bounds from measured source size; row and byte clamps apply per source. Use a bounded
42
60
  preview for unmeasured sources. Preserve the agreed semantics and quality checks.
@@ -72,12 +90,31 @@ merely to obtain a green run. Read [recovery details](references/agent-protocol.
72
90
  8. Read `mr-data checks RUN_ID --json`, `mr-data receipt RUN_ID --json` and
73
91
  `mr-data peek RUN_ID --json`; use bounded `mr-data query` for agreed quality questions.
74
92
  Verify row grain, joins, requested fields, coverage and every declared check. Successful
75
- execution alone is not a verified dataset. Read
93
+ execution alone is not a verified dataset. `receipt` carries one coverage entry per source --
94
+ `bytes_fetched`, `rows_kept`, `rows_available`, `truncated` and the window -- where the run
95
+ record folds all sources into four numbers. That is what each source ACTUALLY weighed and
96
+ carried, and it is the evidence the refresh decision belongs on rather than the ceiling you
97
+ typed in `limits.max_source_bytes` before anything was fetched. Carry the correction into
98
+ step 10. Read
76
99
  [verification](references/7-interrogate-ask-the-run-what-it-actually-delivered.md).
77
100
  9. Any truncated source means a preview, not the requested complete dataset. Inspect coverage
78
101
  even when the run succeeded. A null coverage window does not mean all history was acquired.
79
102
  Repair only evidenced faults on the same dataset/table and verify the revision again.
80
- Record cadence only when authorized; do not promise freshness without evidence.
103
+ Record cadence only when authorized and `mr-data recipe readiness --json` reports the current
104
+ revision refresh-ready. A blocked mutable snapshot requires an explicit table resync, not a
105
+ scheduled whole-source fallback. Do not promise freshness without evidence.
106
+ A recipe that is not refresh-ready is a design fault to repair, not a state to wait out: read
107
+ `blocking_source_names` and every source's classification in the readiness answer. That list
108
+ is all-or-nothing: when one source states no continuation Studio cannot derive a plan at all,
109
+ so it classifies EVERY source `resync_only` and names every one of them, and a twenty-source
110
+ recipe with one bare address comes back with twenty names rather than the one to repair. Read
111
+ the document to find which of them actually declares nothing rather than revising nineteen
112
+ truthful windows. An EMPTY list beside `refresh_ready: false` is the other answer: every source
113
+ is declared and the plan's SHAPE or its predecessor state is what refuses. Revise against what
114
+ this run measured, register the revision and resync once. The
115
+ revision changes the recipe digest, so the sealed predecessor stops matching and one full
116
+ resync is the price of the repair -- which is why the decision belongs in step 5. A table left
117
+ not refresh-ready never refreshes; it does not become ready later.
81
118
  Download and verify artifacts when the user requested bytes.
82
119
  10. Report rows and coverage actually verified, checks, limitations and the stable dataset link.
83
120
  Read [delivery](references/9-present-only-what-survived-inspection-with-caveats.md) for details.
@@ -292,6 +292,94 @@ names.
292
292
  Leaving it out is a defect in the same way a one-line dataset description is: the source is then
293
293
  drawn with its endpoint alone, and a reader is left to guess from a query string.
294
294
 
295
+ ### Say how each source refreshes
296
+
297
+ Studio reads one refresh strategy per source out of this document, and a source that states none
298
+ is `resync_only`. One `resync_only` source makes the whole table resync-only: it never refreshes
299
+ on a schedule, however truthful its siblings are.
300
+
301
+ | what the source declares | classification | next action |
302
+ | --- | --- | --- |
303
+ | `source_class: "stream"` | `recorded` | `recorded` |
304
+ | `closed: true` | `closed` | `reuse_predecessor` |
305
+ | a `collection` | `collection` | `acquire_incremental` |
306
+ | a `window` carrying a `request` | `window` | `acquire_incremental` |
307
+ | none of those | `resync_only` | `resync_required` |
308
+
309
+ The rows are in Studio's own precedence order -- stream, then `closed`, then `collection`, then
310
+ `window` -- because a source may declare more than one of those members and the first match wins.
311
+ A source carrying both `closed: true` and a request window is `closed`, and its window is never
312
+ read. A recorded stream that also says `closed: true` is `recorded`, not `closed`.
313
+
314
+ `closed: true` says the range at this address is over and a refresh may reuse the bytes the
315
+ predecessor sealed rather than ask again. Use it for a range that has demonstrably ended, and
316
+ understand what you are accepting: if the publisher later restates that period, the table keeps
317
+ serving the old bytes, no run fails, and only an explicit resync repairs it. Never mark the
318
+ current period closed -- that drops new data outright, which is a different and worse mistake.
319
+
320
+ A `window` is the better answer wherever the address carries time, because it re-requests its
321
+ recent range on every refresh and so picks up corrections inside `lookback_seconds` on its own.
322
+ It needs `start_at`, `granularity`, `timezone`, `lookback_seconds`, `max_span_seconds` and
323
+ `merge`, and a `request` naming BOTH ends -- an address that names only one side of a range
324
+ cannot be narrowed. Each grammar below is closed; a spelling outside it belongs to a later
325
+ version and is refused.
326
+
327
+ - `request.start` and `request.end` take `encoding`: `date_parts` (names three parameters and
328
+ takes `pad`), `iso_date` or `epoch_seconds` (each names one parameter and takes no `pad`).
329
+ - `bound` is `inclusive` or `exclusive`. The engine's own window is half-open
330
+ `[start_inclusive, end_exclusive)` in UTC; start/inclusive and end/exclusive pass both instants
331
+ through unchanged, and the other two spellings shift by a day. **Probe the publisher rather
332
+ than assuming**: both spellings validate against the schema, and only the start is checked at
333
+ registration, so a wrong `end` silently fetches a different range than the address states.
334
+ - `merge.materialization` is `partition_replace` only.
335
+ - `merge.partition.column` names a column of the ACQUIRED relation -- the header the publisher
336
+ sent, not the name your statement gives it. **That holds only while the window is the recipe's
337
+ one source.** In a plan of more than one source the column must ALSO be a name `table.columns`
338
+ declares, so a renamed field is refused at admission and has to take the one-source route. If
339
+ this recipe has, or will have, a second source of any kind -- a `closed` sibling is enough --
340
+ partition on a name the table itself declares.
341
+ - `merge.partition.key` is `iso_date_prefix` (first ten characters of an ISO value),
342
+ `iso_date_value` (a complete ISO date) or `compact_utc_hour` (a real `YYYYMMDDHH`).
343
+ - `merge.row_identity` names the columns the merged relation must be unique on.
344
+
345
+ `start_at` is a UTC midnight, and the start parameters already written in the address must render
346
+ exactly that instant. If the address says `year1=2026&month1=5&day1=31`, `start_at` is
347
+ `2026-05-31T00:00:00Z`.
348
+
349
+ Declaring nothing is the honest answer for a mutable address with no changed-since contract, and
350
+ pagination is not one. Say which sources you left `resync_only` and why, because a recipe that
351
+ never asked the question and one that asked and answered `resync_only` register identically.
352
+
353
+ **Declaring every source is the FIRST gate, and the SHAPE of the plan is the second.** Once Studio
354
+ has one action per source it asks whether a worker can materialize the plan those actions make,
355
+ and admits two families only: every source a recorded stream, which appends sealed batches, or at
356
+ least one `window` carrying a `request`, with `closed` siblings permitted beside it, which replaces
357
+ the bounded partitions the window asked for. A closed-only plan refreshes nothing -- it restores
358
+ the predecessor's bytes and leaves no partition to replace. Any `collection` in the plan refuses
359
+ the whole table, alone or beside a window. A `window` beside a recorded stream refuses it too. Each
360
+ of those is `RESYNC_REQUIRED` before acquisition: the table registers, builds and serves, and only
361
+ an explicit resync ever moves it again. So `closed: true` alone does NOT make a table refresh, and
362
+ marking every source closed to clear the first warning buys a table that is just as dead and warns
363
+ about nothing.
364
+
365
+ **The second family is NECESSARY and not sufficient, and the count is of sources.** A plan holding
366
+ more than one source -- one `window` and one `closed` sibling is already two -- is materialized by
367
+ the several-source lane, which writes ONE table manifest. Every window in such a plan must
368
+ therefore agree with the others on `merge.materialization`, `merge.partition.column` and
369
+ `merge.partition.key`, and each partition column must be a name `table.columns` declares.
370
+ Disagreeing windows, and a partition column the table does not declare, are both
371
+ `RESYNC_REQUIRED`. So adding a `closed` sibling beside a lone window is not free: it is what turns
372
+ the declared-column requirement on.
373
+
374
+ **Decide it from MEASUREMENT, not from the ceiling you typed.** `limits.max_source_bytes` is a
375
+ bound written before anything was fetched, and it is wrong in both directions: one live publisher
376
+ came in an order of magnitude under the ceiling declared for it, and another came in over. The
377
+ run's own coverage is the other thing entirely -- `mr-data receipt RUN --json` carries one entry
378
+ per source with `bytes_fetched`, `rows_kept`, `rows_available` and `truncated`, which is what that
379
+ source actually weighed and how many rows it actually carried. Build once, read that, and correct
380
+ the declarations against it before you record a cadence. A revision changes the recipe digest and
381
+ costs one full resync, so the cheapest time to be right is here.
382
+
295
383
  ### Say what each column is, and what it shows
296
384
 
297
385
  **Every column carries a `description`, and every column that measures a physical quantity carries
@@ -181,11 +181,21 @@ the record — `mr-data run --recipe RECIPE_ID --digest RECIPE_DIGEST --full --c
181
181
  held answer prints back as `confirm_command`. Any authenticated surface of the paying workspace may
182
182
  settle it. `mr-data run --cancel RUN_ID` stops a run that is queued or running.
183
183
 
184
- Do not treat the accepted command vocabulary as execution evidence. Author a source window only
185
- when the publisher has a truthful bounded date endpoint: use query names by default or exact
186
- `{parameter}` markers in its path, retain a correction lookback, and keep historical sources
187
- revalidatable unless an explicit closure contract says they cannot change. A refresh merges each
188
- windowed source slice into retained source state and recomputes the complete transform; unrelated
189
- snapshot sources are conditionally revalidated or re-acquired as needed. Cursor, pagination and
190
- new-file discovery need their own supported source contract and must not be represented as a date
191
- window. A `backfill` may be refused before acquisition; the refusal says so.
184
+ Do not treat the accepted command vocabulary as execution evidence. Before proposing a schedule,
185
+ classify every source under one truthful continuation strategy and record the supporting evidence.
186
+ Normal refresh materializes an all-recorded-stream recipe, or a plan holding at least one direct
187
+ `window.request` source with a compatible partitioned predecessor, `closed` reuse permitted beside
188
+ it. Beside it is not free: as soon as the plan holds more than one source, every window in it must
189
+ agree on one `merge.materialization`, `merge.partition.column` and `merge.partition.key`, and that
190
+ column must be a name the table declares. The classifier may name closed reuse or collection
191
+ continuation, but closed-only plans, any plan carrying a collection, and a window beside a
192
+ recorded stream return `RESYNC_REQUIRED` before acquisition because their bounded table
193
+ materializers do not exist yet. A `window.snapshot` or unwindowed mutable source is likewise
194
+ resync-only. Explicit resync is full,
195
+ has no predecessor, and a collection starts from the beginning. Never hide one behind conditional
196
+ revalidation, generic pagination, or a whole-source comparison. Research publisher cursors,
197
+ revision identities, listings, corrections, and deletions, but use only deployed recipe grammar
198
+ that represents the proven strategy. For each incremental source, record the affected key or
199
+ partition and a fixture proving
200
+ that applying its delta to the predecessor produces the same rows as a full build over
201
+ predecessor-plus-delta. A `backfill` may be refused before acquisition; the refusal says so.
@@ -14,7 +14,7 @@ gap rather than doing anything, and `export-hosted-candidate`, which is a backen
14
14
  | `mr-data dataset` | Bring the dataset page into existence before there is anything on it, then fill it in while somebody watches. `dataset create --name TEXT` mints it and prints the `dataset_id`; `dataset show ID` reads it back; `dataset set ID --name TEXT --topics "a,b,c" --license ID --description-file F` writes the title, the descriptive tags, the SPDX licence and the description under the version it was read at, retrying once if somebody else wrote first, and an empty `--topics` or `--license` takes that value off the page; a saved write whose public sync fails exits 2 and reports `public_projection_synced: false` — use `dataset sync ID` to retry that sync without rewriting Studio; `dataset note ID --heading H --blocks-file B` writes one cell of the decision record that OUTLIVES every run, and `--list` reads it back; `dataset watch ID` follows the page's own event stream; `dataset activity ID --phase P --message TEXT` says what is happening right now, silently, and is never a chat message and never a cell; `dataset publish ID [--mode public|link|private]` says who can read the dataset — `public` lists it in the public directory and serves it at an address anybody can read, `link` serves it at an unlisted address, `private` takes it back to the workspace — and `dataset publish ID --show` reads that back without changing it; `dataset archive ID --confirm-name TITLE` retires the page and frees its title, deleting nothing. |
15
15
  | `mr-data recipe` | Register one recipe document — dataset, question, table plan, sources, transform, checks and units — in one call, and print the identifiers the server derived. The document is read first and every fault comes back in one refusal, each with the JSON pointer that names it; `--no-lint` sends it as written instead. Registration refuses `THIN_BRIEF_MISSING` when the dataset's record carries no answered question and no delegation, and `THIN_RECIPE_DATASET_UNBOUND` when the document names no `dataset.id`; `--delegated "their words"` writes the delegation onto the dataset and registers in the same command, and clears the first of those two and never the second. `mr-data recipe show ID` reads one back. |
16
16
  | `mr-data cover` | Generate and attach one branded 1200×630 dataset cover. Give it the dataset ID and what the image should depict; Studio fixes the model, single-color style, curated random palette, dimensions and storage. |
17
- | `mr-data run` | Start one run against a registered recipe, named as `--recipe RECIPE_ID --digest RECIPE_DIGEST` — both required, neither positional, and the digest is the bare hex the registration receipt printed. The mode is one of six: `sample`, `full`, `refresh`, `backfill`, `compact` and `replay`. Four have a shorthand flag (`--sample`, `--full`, `--refresh`, `--backfill`) and two do not, so write `--mode MODE`, which is accepted for every one of them. A refresh narrows each source that declares a supported address window and merges its returned relation into retained source state; unwindowed sources are conditionally revalidated or re-acquired whole, then the full declared transform runs. `--backfill` states the exact window with `--window START END`. `--mode replay --sources-from RUN_ID` asks Studio to run the registered revision against the RETAINED RAW INPUTS of one named successful run of the same table: nothing is fetched from this computer, nothing is acquired again, the result is compared against that run and never becomes the live version, and it neither asks for nor records an approval. Where Studio has replay switched off, or the named run is not successful, not the same table, or no longer retains its inputs, it answers a typed refusal — report the code rather than retrying in another mode. `--sources-from` on any other mode is refused `THIN_ARGUMENT_INVALID` before anything is sent. `--max-rows` and `--max-source-bytes` bound ONE SOURCE rather than the finished table. A large run is held for a spend confirmation, which `--confirm` settles and whose printed `confirm_command` re-states every argument the request carried. `--approve-full RUN_ID` releases the full a sample-first pair is holding once its preview has succeeded, naming either half of the pair; where the deployment still wants a person at a browser it answers `THIN_INTERACTIVE_HUMAN_REQUIRED` and names the run page. `--retry RUN_ID` tries one failed run again when its triple carries `room_fault: true`, re-stating that run's own coordinate under a byte ceiling that cannot narrow it, and refuses with the triple when the recipe was at fault. `--cancel RUN` stops one. |
17
+ | `mr-data run` | Start one run against a registered recipe, named as `--recipe RECIPE_ID --digest RECIPE_DIGEST` — both required, neither positional, and the digest is the bare hex the registration receipt printed. The mode is one of six: `sample`, `full`, `refresh`, `backfill`, `compact` and `replay`. Four have a shorthand flag (`--sample`, `--full`, `--refresh`, `--backfill`) and two do not, so write `--mode MODE`, which is accepted for every one of them. A refresh executes only Studio's persisted strict source-action plan. Although the classifier may name closed reuse, a request-window or collection delta, or recorded input, current normal refresh materializes only a plan carrying at least one direct `partition_replace` request window with a compatible predecessor — `closed` siblings may reuse their sealed bytes beside it, a lone window needs a retained source-history record, and several must agree on materialization and partition layout — or an all-recorded-stream plan. Closed-only, any plan carrying a collection, a window beside a recorded stream, snapshot and unwindowed plans return `RESYNC_REQUIRED` before acquisition. Use `mr-data table resync TABLE_ID --request-id UUID` for an explicit full source reread, retaining that UUID if the response is uncertain. `--backfill` states the exact window with `--window START END`. `--mode replay --sources-from RUN_ID` asks Studio to run the registered revision against the RETAINED RAW INPUTS of one named successful run of the same table: nothing is fetched from this computer, nothing is acquired again, the result is compared against that run and never becomes the live version, and it neither asks for nor records an approval. Where Studio has replay switched off, or the named run is not successful, not the same table, or no longer retains its inputs, it answers a typed refusal — report the code rather than retrying in another mode. `--sources-from` on any other mode is refused `THIN_ARGUMENT_INVALID` before anything is sent. `--max-rows` and `--max-source-bytes` bound ONE SOURCE rather than the finished table. A large run is held for a spend confirmation, which `--confirm` settles and whose printed `confirm_command` re-states every argument the request carried. `--approve-full RUN_ID` releases the full a sample-first pair is holding once its preview has succeeded, naming either half of the pair; where the deployment still wants a person at a browser it answers `THIN_INTERACTIVE_HUMAN_REQUIRED` and names the run page. `--retry RUN_ID` tries one failed run again when its triple carries `room_fault: true`, re-stating that run's own coordinate under a byte ceiling that cannot narrow it, and refuses with the triple when the recipe was at fault. `--cancel RUN` stops one. |
18
18
  | `mr-data status` | Report which of the seven states one run is in, what it delivered and whether a ceiling cut it short, and — on a failure — what failed, where, and whether the fault was the execution room's rather than the recipe's. Exits non-zero on a failed run. |
19
19
  | `mr-data runs` | Report this workspace's own runs: identifier, mode, state and creation time, plus the failure triple of any that failed and whether that failure was the execution room's. `--status` and `--mode` narrow it and are refused `THIN_ARGUMENT_INVALID` for a word outside the seven states or the six modes; `--limit` says how many, and pages are followed to reach it. A narrow filter over a workspace of thousands of runs reads a long way to find its matches, and a listing that does not end says how many were read and suggests a smaller `--limit`. |
20
20
  | `mr-data watch` | Stream one run's live progress under the durable event type of each stage, resuming across stream cuts. Exits non-zero when the run failed. |
@@ -29,7 +29,7 @@ gap rather than doing anything, and `export-hosted-candidate`, which is a backen
29
29
  | `mr-data diff` | Compare two runs and say what changed. |
30
30
  | `mr-data connections` | List saved workspace connections and their current recipe coordinates; `--dataset ID` filters to connections granted to that dataset. No credential values are returned. |
31
31
  | `mr-data keys` | Enrol a source credential by name, list the names with their created and rotated times, and delete one. `keys set` reads the value from a file or from standard input and never from the command line; no command prints a value back. |
32
- | `mr-data stream` | Record a public `wss://` venue so a build can read it. `stream document register --file F` admits the connector document, `stream registry create --digest D` admits a set of them, `stream probe` listens briefly and seals nothing, `stream capture --seconds N` records a bounded window, `stream subscribe` keeps recording, `stream subscription {show|pause|resume|stop} ID` moves one, `stream status ID` reads a capture or a probe, and `stream recordings --document-digest D` names what a build could read and until when. `--seconds` is at most 600. `mr-data run` takes no duration: a build reads sealed batches and is bounded by them. |
32
+ | `mr-data stream` | Record a public `wss://` venue so a build can read it. `stream document register --file F` admits the connector document, `stream registry create --digest D` admits a set of them, `stream probe` listens briefly and seals nothing, `stream capture --seconds N` records a bounded window, and `stream subscribe` keeps recording. `stream subscription {show|pause|resume|stop} ID` retains the legacy full-record interface. `stream subscription quiescence ID` reads the bounded server-computed proof; `pause-cas` and `resume-cas` read its ETag and make one conditional bounded write. An ambiguous CAS failure is not retried until a fresh invocation observes the resource again. `stream status ID` reads a capture or probe, and `stream recordings --document-digest D` names what a build could read and until when. `--seconds` is at most 600. `mr-data run` takes no duration: a build reads sealed batches and is bounded by them. |
33
33
  | `mr-data open` | Mint a single-use, short-lived address that continues this session in a browser at one dashboard path, and print it. `--open` hands it to the platform opener. The credential decides who mints: a device credential — what `mr-data login` stores — mints at Cloud, and a personal access token mints at Studio. |
34
34
  | `mr-data promote` | Record the cadence one table refreshes on and the reasoning behind it. A table's first succeeded run goes live on its own, so this is not what makes it readable; it is idempotent on a table that is already live, and it is how a withdrawn table is put back. The Harness worker does not perform the catch-up or scheduled refresh; report those only when another deployed component returns durable evidence that it did. |
35
35
  | `mr-data reschedule` | Change how often one live table refreshes, without the demote and promote that would reset its bookkeeping and buy a catch-up run. `--cadence` takes a cron expression or an interval such as `every 6h`; `--why` records the reasoning. It starts no run. `--lock` freezes the schedule so Studio stops adjusting it; `--unlock` lets it follow the source again. |
@@ -38,6 +38,24 @@ gap rather than doing anything, and `export-hosted-candidate`, which is a backen
38
38
  | `mr-data unpin` | Ask Studio to resume pointer tracking; read the returned state before claiming it resumed. |
39
39
  | `mr-data demote` | Ask Studio to withdraw the pointer and any schedule it owns, for one table or for as many as you name: `demote TABLE [TABLE ...]` withdraws them one after another, attempts every one of them whatever the one before it answered, prints a line for each, and exits non-zero if any is still live. Pair it with `table archive` when you are retiring a set: a live table cannot be archived, so it is withdraw-then-archive, two commands rather than a loop. |
40
40
 
41
+ ### Strict refresh controls
42
+
43
+ `mr-data recipe readiness` pages Studio's immutable-revision inventory and reports predecessor,
44
+ incremental-materialization, and differential-proof readiness, every source's classified next
45
+ action, and blocking source names. It reads persisted facts; it does not authorize a schedule.
46
+
47
+ A refresh executes only Studio's persisted source-action plan. The classifier may name closed
48
+ reuse, a request window or collection delta, or recorded input, but current normal refresh
49
+ materializes only a plan carrying at least one direct `partition_replace` request window with a
50
+ compatible predecessor, or an all-recorded-stream plan. `closed` siblings may reuse their sealed
51
+ bytes beside a window; a lone window needs a retained source-history record, and several windows
52
+ must agree on materialization and partition layout. Closed-only, any plan carrying a collection, a
53
+ window beside a recorded stream, snapshot and unwindowed plans return `RESYNC_REQUIRED` before
54
+ acquisition. `mr-data table resync TABLE --request-id UUID` is the explicit
55
+ full reread; retain and reuse the caller-generated UUID after a lost response. A held resync receipt
56
+ prints `mr-data run --confirm-held RUN_ID` for that exact run. A sample-first resync instead uses
57
+ `--approve-full` only after its preview succeeds.
58
+
41
59
  `auth`, `login`, `whoami` and `clarify` are the same implementation in both profiles, and
42
60
  `clarify` alone reaches nothing at all. The other twenty-eight answer from Studio. Twelve only
43
61
  read: `status`, `runs`, `watch`, `peek`, `receipt`, `checks`, `download`, `verify`, `parts`,
@@ -57,6 +57,14 @@ full of a sample-first pair: its bounded preview must seal a downloadable table
57
57
  though it could. Keep the raw state internal: say what product decision is needed and give the
58
58
  person the single action that lets the build continue.
59
59
 
60
+ **`build_scope` says what the build covers, not what it is doing.** A `failed` or `cancelled` run
61
+ covers nothing and says so. A `succeeded` one reports what `coverage.truncated` recorded. Anything
62
+ else states the bound it ran under — a `sample` its ceilings, a `full` the whole dataset, and a
63
+ `full` at `awaiting_sample_approval` the whole dataset plus the preview it waits on. The mode
64
+ alone never establishes a truncation, which is the rule reference 6 states: a sample sized above
65
+ every measured source cut nothing and is the build. It is absent on `refresh`, `backfill`,
66
+ `compact` and `replay`, and it is never evidence that a run is executing; `status` is.
67
+
60
68
  **A failed run always carries the same three members** — `failure_code`, `failure_detail` and
61
69
  `failed_stage` — on three surfaces: the run record, the terminal event on the stream, and every
62
70
  refusal a command raises about it. `mr-data status` and `mr-data runs` add a fourth reading beside
@@ -44,13 +44,19 @@ Studio stops adjusting it, which is how a person overrides the evidence; `--unlo
44
44
  the source again. Do not lock a schedule on your own judgement — it is the same kind of decision as
45
45
  the cadence itself, and it belongs to the user.
46
46
 
47
- This call records a schedule; it is not proof of data continuation. The current Harness worker
48
- can execute a supported refresh, including declared source windows, but it does not make a frozen
49
- date, a generic cursor or a file listing advance by inference. It also does not attach or execute a
50
- warehouse schedule. Another deployed Studio or warehouse component may do that work, but only its
51
- returned state and durable run evidence support a claim that the table is live, caught up, or
52
- refreshing. If that evidence is absent, report that a schedule was recorded and that ongoing
53
- freshness is unavailable; do not imply that recording it filled the gap.
47
+ This call records a schedule; it is not proof of data continuation. Schedule only a current recipe
48
+ that `mr-data recipe readiness --json` reports as refresh-ready. Studio must have a persisted bounded
49
+ action for every source. The classifier may name closed reuse, a request-window or collection
50
+ delta, or recorded stream input, but normal refresh admits only an all-recorded-stream plan, or a
51
+ plan holding at least one direct partition request window with a compatible predecessor, `closed`
52
+ reuse permitted beside it. A plan of more than one source is admitted only when its windows agree
53
+ on one `merge.materialization`, `merge.partition.column` and `merge.partition.key`, and that
54
+ column is a name the table declares. Closed-only, collection, snapshot, and unwindowed plans, and
55
+ a window beside a recorded stream, return `RESYNC_REQUIRED` before acquisition. Use `mr-data
56
+ table resync TABLE_ID --request-id UUID` when a full reread is genuinely needed; it is an
57
+ explicit run, not a schedule fallback. Only returned state and durable run evidence support a
58
+ claim that the table is caught up or refreshing. If that evidence is absent, report that a
59
+ schedule was recorded and that ongoing freshness is unavailable.
54
60
 
55
61
  `mr-data pin TABLE_ID --version VERSION_ID` freezes the pointer on an exact version for a rollback
56
62
  or a hold, `mr-data unpin TABLE_ID` resumes tracking, and `mr-data demote TABLE_ID` detaches the
@@ -24,8 +24,9 @@ https://mostlyright.md/docs/recipes/ lists all thirteen: `city-temperatures`, `c
24
24
  CSV file, one table), `json-api` (records pointer and pagination), `html-collection` (many HTML
25
25
  pages, one table), `document-extraction` (PDF projection), `weather-grib` (GRIB2 and the scientific
26
26
  Readers), `websocket-stream` (a `wss://` venue), `multi-source-join` (two sources joined, with
27
- checks), `market-settlement-join`, `forecast-vs-observation`, `snapshot-window` (a listing replaced
28
- on refresh), `many-station-weather` (twenty station feeds unioned) and `stream-to-bars`.
27
+ checks), `market-settlement-join`, `forecast-vs-observation`, `snapshot-window` (a point-in-time
28
+ source whose normal refresh currently requires explicit resync), `many-station-weather` (twenty
29
+ station feeds unioned) and `stream-to-bars`.
29
30
 
30
31
  The build guides run one per stage, at https://mostlyright.md/docs/build/NAME/ where NAME is
31
32
  `probe-sources`, `write-a-recipe`, `run-and-inspect`, `publish-and-refresh`, `credentials`,
@@ -20,15 +20,23 @@
20
20
  - Preserve event time, available time, acquisition time, source revision, and timezone. Do not use
21
21
  post-cutoff information in targets, labels, features, joins, or validation decisions.
22
22
  - When the question is **what changed over time**, or the table needs point-in-time features, and
23
- the source answers only with the present, declare a `snapshot` window on that source: Studio's
24
- worker stamps every row with the UTC day it acquired the source on and keeps one partition per
25
- day. Compute a transition with
23
+ the source answers only with the present, a `snapshot` window expresses the intended future
24
+ daily partition model. It is not executable as normal refresh in this release: Studio returns
25
+ `RESYNC_REQUIRED` before acquisition. Explicit resync is full with no predecessor and replaces
26
+ the table with the current observation; it does not retain earlier days. Once the bounded
27
+ snapshot materializer ships, compute a transition with
26
28
  `lag(column) over (partition by <identity> order by <snapshot column>)` and keep
27
29
  the rows where the two are `is distinct from` each other. Never diff two sealed versions by hand;
28
30
  a version is not a date, and nothing outside the seal can be replayed.
29
31
 
30
32
  ### Authoring a collection source
31
33
 
34
+ Current execution boundary: an initial full or explicit table resync may acquire a collection,
35
+ but collection-only and mixed normal refreshes return `RESYNC_REQUIRED` before acquisition. An
36
+ explicit resync is full, has no predecessor, and starts collection discovery from the beginning.
37
+ The ledger continuation rules below describe historical receipts and a future bounded materializer,
38
+ not a current normal-refresh action.
39
+
32
40
  A corpus published as an index plus many detail pages is **one** source through
33
41
  `public.https.collection@2.0.0`, not one source per page. Source count and publisher count are
34
42
  unaffected by how many pages sit behind it: 2,819 pages is one source, one publisher, one line on
@@ -79,17 +87,16 @@ column reaches the transform as `VARCHAR`; selecting `page_ordinal` bare while d
79
87
  `TIMESTAMP WITH TIME ZONE` and nothing else.
80
88
 
81
89
  **A listing that could not be read says so.** The coverage block carries `discovery_failure`:
82
- `null`, or the code and one sentence for the first listing request the run was refused. A refresh
83
- that already holds pages keeps every row and succeeds; a FIRST run whose listing is refused has
84
- nothing to continue from and fails with `COLLECTION_DISCOVERY_FAILED` rather than sealing an empty
85
- corpus. If a run reports zero discovered, read that member before concluding the publisher's index
86
- is empty.
90
+ `null`, or the code and one sentence for the first listing request the run was refused. A full
91
+ resync whose listing is refused has nothing to continue from and fails with
92
+ `COLLECTION_DISCOVERY_FAILED` rather than sealing an empty corpus. If a run reports zero
93
+ discovered, read that member before concluding the publisher's index is empty.
87
94
 
88
- **Expect the first run not to finish, and say so.** A run that spends its fetch budget still
89
- SUCCEEDS; its coverage block reports `complete: false` and names the budget that ended it. The
90
- next refresh continues from the ledger — new pages first, then failures oldest first, then
91
- revisits — and every row of a page this run did not fetch survives byte for byte. Report the
92
- shortfall as what it is, a backfill in progress, not as a failure and not as a complete corpus.
95
+ **A partial full/resync result must be reported as partial.** A run that spends its fetch budget
96
+ still SUCCEEDS; its coverage block reports `complete: false` and names the budget that ended it.
97
+ Do not call that a backfill in progress: no current normal refresh can continue the ledger, and a
98
+ later explicit resync starts discovery over. Report it as an incomplete corpus and either accept
99
+ that limitation or revise the bounded recipe before another explicit resync.
93
100
 
94
101
  A page that disappears from the index deletes nothing. A page answering with different content
95
102
  replaces exactly its own rows, increments `page_revision`, and keeps the previous digest: that is
@@ -37,9 +37,9 @@ __all__ = [
37
37
  #: ``{"code", "detail"}`` of the first listing request this run could not read. It is stated
38
38
  #: because a discovery that failed and a corpus that is empty produce the same counts, and a
39
39
  #: reader with only the counts cannot tell an unreachable index from a publisher who has stopped
40
- #: publishing. A run that had nothing to continue from does not report it -- it fails, under
41
- #: ``COLLECTION_DISCOVERY_FAILED`` -- so a block carrying it is always a refresh that kept its
42
- #: rows.
40
+ #: publishing. A full/resync run with no listing and no rows fails under
41
+ #: ``COLLECTION_DISCOVERY_FAILED``. Historical refresh receipts may carry the member after keeping
42
+ #: predecessor rows, but current collection refresh is refused before acquisition.
43
43
  PAGE_COVERAGE_FIELDS: tuple[str, ...] = (
44
44
  "discovered",
45
45
  "discovery_requests",
@@ -103,5 +103,5 @@ def page_coverage_sentence(block: Mapping[str, Any]) -> str:
103
103
  parts.append(f"the page listing could not be read ({listing['code']})")
104
104
  if failed:
105
105
  parts.append(f"{failed:,} failed")
106
- parts.append("continues on the next refresh")
106
+ parts.append("requires another explicit resync, which starts from the beginning")
107
107
  return "; ".join(parts)