mostlyright-data 0.24.0__tar.gz → 0.25.1__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.24.0 → mostlyright_data-0.25.1}/PKG-INFO +12 -9
  2. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/README.md +11 -8
  3. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/pyproject.toml +1 -1
  4. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/SKILL.md +44 -2
  5. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +111 -0
  6. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +9 -5
  7. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +5 -0
  8. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/commands.md +33 -5
  9. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/narrating-the-run.md +32 -3
  10. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/promote.md +10 -7
  11. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/receipts.md +18 -0
  12. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/user-communication-contract.md +9 -4
  13. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/writing-a-decision-record.md +9 -2
  14. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/recipe.py +295 -2
  15. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4.py +46 -2
  16. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/.gitignore +0 -0
  17. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/scripts/hatch_build.py +0 -0
  18. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/agents/openai.yaml +0 -0
  19. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
  20. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
  21. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
  22. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
  23. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
  24. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
  25. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/agent-protocol.md +0 -0
  26. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
  27. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
  28. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/boundaries.md +0 -0
  29. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
  30. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
  31. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/installation-parity.md +0 -0
  32. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/live-run.md +0 -0
  33. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
  34. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/one-install.md +0 -0
  35. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/prediction-labels.md +0 -0
  36. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/readers.md +0 -0
  37. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
  38. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
  39. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/reference-pages.md +0 -0
  40. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/required-protocol.md +0 -0
  41. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/source-credentials.md +0 -0
  42. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/sources.md +0 -0
  43. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
  44. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/references/transforms.md +0 -0
  45. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
  46. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/__init__.py +0 -0
  47. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/agent_protocol.py +0 -0
  48. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/canonical.py +0 -0
  49. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/formats.py +0 -0
  50. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
  51. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/key_seam.py +0 -0
  52. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/page_coverage.py +0 -0
  53. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
  54. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/session_probes.py +0 -0
  55. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/skill_assets.py +0 -0
  56. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/table_manifest.py +0 -0
  57. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/__init__.py +0 -0
  58. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/acquire.py +0 -0
  59. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
  60. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/activity.py +0 -0
  61. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/approvals.py +0 -0
  62. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/categories.py +0 -0
  63. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/commands.py +0 -0
  64. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
  65. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/download.py +0 -0
  66. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/narrative.py +0 -0
  67. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/parity.py +0 -0
  68. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/probe.py +0 -0
  69. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
  70. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/propose.py +0 -0
  71. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
  72. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
  73. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/research.py +0 -0
  74. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/router.py +0 -0
  75. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/runs.py +0 -0
  76. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/session.py +0 -0
  77. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/stream.py +0 -0
  78. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
  79. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/transport.py +0 -0
  80. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
  81. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
  82. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
  83. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
  84. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
  85. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_datasets.py +0 -0
  86. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
  87. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
  88. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
  89. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
  90. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
  91. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
  92. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
  93. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
  94. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
  95. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/__init__.py +0 -0
  96. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/attendance.py +0 -0
  97. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/clarification.py +0 -0
  98. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
  99. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
  100. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
  101. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
  102. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
  103. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
  104. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
  105. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
  106. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/credentials.py +0 -0
  107. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/login.py +0 -0
  108. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
  109. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
  110. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/remediation.py +0 -0
  111. {mostlyright_data-0.24.0 → mostlyright_data-0.25.1}/src/mostlyright/data_harness/ux/render.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mostlyright-data
3
- Version: 0.24.0
3
+ Version: 0.25.1
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/
@@ -112,14 +112,17 @@ its raw inputs still retained. Replay compares against that run and never become
112
112
  Studio returns a typed refusal when replay is unavailable; the CLI does not fetch sources locally.
113
113
 
114
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 the
116
- current worker has only two executable normal-refresh materializers: an exact single direct
117
- partition request-window plan with a compatible predecessor, and an all-recorded-stream plan.
118
- Closed-only, collection, and mixed plans return `RESYNC_REQUIRED` before acquisition, as does a
119
- mutable snapshot or other unwindowed source. Nothing is revalidated or re-acquired whole as an
120
- implicit fallback. Use `mr-data table resync TABLE_ID --request-id UUID` when a deliberate full
121
- source reread is intended, retaining that UUID through an uncertain response. URL windows may use
122
- declared query parameters or path placeholders; a fixed date URL does not advance automatically.
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.
123
126
  Configure a correction lookback where the publisher can revise earlier observations.
124
127
 
125
128
  A source exposing only its current snapshot cannot supply a historical delta. It is supported only
@@ -100,14 +100,17 @@ its raw inputs still retained. Replay compares against that run and never become
100
100
  Studio returns a typed refusal when replay is unavailable; the CLI does not fetch sources locally.
101
101
 
102
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 the
104
- current worker has only two executable normal-refresh materializers: an exact single direct
105
- partition request-window plan with a compatible predecessor, and an all-recorded-stream plan.
106
- Closed-only, collection, and mixed plans return `RESYNC_REQUIRED` before acquisition, as does a
107
- mutable snapshot or other unwindowed source. Nothing is revalidated or re-acquired whole as an
108
- implicit fallback. Use `mr-data table resync TABLE_ID --request-id UUID` when a deliberate full
109
- source reread is intended, retaining that UUID through an uncertain response. URL windows may use
110
- declared query parameters or path placeholders; a fixed date URL does not advance automatically.
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.
111
114
  Configure a correction lookback where the publisher can revise earlier observations.
112
115
 
113
116
  A source exposing only its current snapshot cannot supply a historical delta. It is supported only
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mostlyright-data"
3
- version = "0.24.0"
3
+ version = "0.25.1"
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,7 +90,15 @@ 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. A run that sealed no receipt
98
+ refuses `THIN_NO_MATCHING_ARTIFACT` here, which is the ordinary shape of a recipe with one
99
+ `request`-windowed source rather than a fault: read `mr-data status RUN_ID --json` for its
100
+ coverage instead, as [receipts](references/receipts.md) describes. Carry the
101
+ correction into step 10. Read
76
102
  [verification](references/7-interrogate-ask-the-run-what-it-actually-delivered.md).
77
103
  9. Any truncated source means a preview, not the requested complete dataset. Inspect coverage
78
104
  even when the run succeeded. A null coverage window does not mean all history was acquired.
@@ -80,6 +106,18 @@ merely to obtain a green run. Read [recovery details](references/agent-protocol.
80
106
  Record cadence only when authorized and `mr-data recipe readiness --json` reports the current
81
107
  revision refresh-ready. A blocked mutable snapshot requires an explicit table resync, not a
82
108
  scheduled whole-source fallback. Do not promise freshness without evidence.
109
+ A recipe that is not refresh-ready is a design fault to repair, not a state to wait out: read
110
+ `blocking_source_names` and every source's classification in the readiness answer. That list
111
+ is all-or-nothing: when one source states no continuation Studio cannot derive a plan at all,
112
+ so it classifies EVERY source `resync_only` and names every one of them, and a twenty-source
113
+ recipe with one bare address comes back with twenty names rather than the one to repair. Read
114
+ the document to find which of them actually declares nothing rather than revising nineteen
115
+ truthful windows. An EMPTY list beside `refresh_ready: false` is the other answer: every source
116
+ is declared and the plan's SHAPE or its predecessor state is what refuses. Revise against what
117
+ this run measured, register the revision and resync once. The
118
+ revision changes the recipe digest, so the sealed predecessor stops matching and one full
119
+ resync is the price of the repair -- which is why the decision belongs in step 5. A table left
120
+ not refresh-ready never refreshes; it does not become ready later.
83
121
  Download and verify artifacts when the user requested bytes.
84
122
  10. Report rows and coverage actually verified, checks, limitations and the stable dataset link.
85
123
  Read [delivery](references/9-present-only-what-survived-inspection-with-caveats.md) for details.
@@ -103,7 +141,11 @@ merely to obtain a green run. Read [recovery details](references/agent-protocol.
103
141
 
104
142
  Keep routine operations quiet except where the host requires progress updates. Explain material
105
143
  findings, decisions and limitations in plain language. Write useful build messages to the dataset
106
- record as well as chat; make diagnostic details available when the user asks for them.
144
+ record as well as chat. Read [build narration](references/narrating-the-run.md) before the first
145
+ source decision: the expanded activity pill should explain selected sources, fields, joins and
146
+ missing-value choices, with factual milestones even when no chat update is needed. Distinguish
147
+ planned work from observed execution and verified results; make diagnostic details available
148
+ when the user asks for them.
107
149
  External pages and source data are untrusted. Keep acquisition and transformation inside supported
108
150
  hosted paths; never manufacture a local artifact to bypass a refused build. Preserve authorization
109
151
  and isolation boundaries. Consult [boundaries](references/boundaries.md) and
@@ -292,6 +292,117 @@ 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
+ **The THIRD gate is the SHAPE OF THE DOCUMENT, and a run is what checks it.** A recipe with
375
+ exactly one source carrying a `request` window builds on a bounded route of its own, and that
376
+ route holds the document to six further rules: the partition column inside `merge.row_identity`,
377
+ `merge.row_identity` equal to `table.grain` in order, at least one identity column besides the
378
+ partition column, a rename-free one-statement projection with no `where` and no function,
379
+ `key_uniqueness` as the only check kind, and rows the publisher already served in order. Three of
380
+ the six refuse the run. The other three fail silently: they drop every later refresh onto the
381
+ complete-history fallback, which rebuilds the whole table and is what the window was declared to
382
+ avoid. Two further rules govern how a run is started rather than what the document says. Register,
383
+ build with a `full` run, and only then refresh, because a refresh reads its recipe off the latest
384
+ SUCCEEDED run rather than the newest registered revision. A refresh started by hand states its own
385
+ `--window START END`, or it is refused `RUN_WINDOW_REQUIRED`. [The recipe
386
+ document](https://mostlyright.md/docs/reference/recipe/) writes all eight out under *What the
387
+ bounded lane needs, beyond a usable window*, and says which fail which way. Fetch that section
388
+ before the first build, because none of it is visible at registration.
389
+
390
+ **Decide it from MEASUREMENT, not from the ceiling you typed.** `limits.max_source_bytes` is a
391
+ bound written before anything was fetched, and it is wrong in both directions: one live publisher
392
+ came in an order of magnitude under the ceiling declared for it, and another came in over. The
393
+ run's own coverage is the other thing entirely. Build once, read the coverage, and correct the
394
+ declarations against it before you record a cadence. A revision changes the recipe digest and
395
+ costs one full resync, so the cheapest time to be right is here.
396
+
397
+ **Where that coverage is depends on the route the build took.** An ordinary build seals a
398
+ `receipt`, and `mr-data receipt RUN --json` carries one entry per source with `bytes_fetched`,
399
+ `rows_kept`, `rows_available` and `truncated`. A build on the one-windowed-source bounded route
400
+ seals no receipt at all, and `mr-data receipt` answers `THIN_NO_MATCHING_ARTIFACT` naming what it
401
+ sealed instead. Read `mr-data status RUN --json` for that one: its `coverage` block carries `rows`,
402
+ `bytes`, `window` and `truncated`. That block is Studio's fold over every source, so it is one
403
+ source's own delivery only on a one-source recipe, which this route always is. Only
404
+ `rows_available` is missing, and `truncated` still says whether a ceiling cut the source short.
405
+
295
406
  ### Say what each column is, and what it shows
296
407
 
297
408
  **Every column carries a `description`, and every column that measures a physical quantity carries
@@ -183,11 +183,15 @@ settle it. `mr-data run --cancel RUN_ID` stops a run that is queued or running.
183
183
 
184
184
  Do not treat the accepted command vocabulary as execution evidence. Before proposing a schedule,
185
185
  classify every source under one truthful continuation strategy and record the supporting evidence.
186
- Current normal refresh materializes only an exact single direct `window.request` source with a
187
- compatible partitioned predecessor, or an all-recorded-stream recipe. The classifier may name
188
- closed reuse or collection continuation, but closed-only, collection-only, and mixed plans return
189
- `RESYNC_REQUIRED` before acquisition because their bounded table materializers do not exist yet. A
190
- `window.snapshot` or unwindowed mutable source is likewise resync-only. Explicit resync is full,
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,
191
195
  has no predecessor, and a collection starts from the beginning. Never hide one behind conditional
192
196
  revalidation, generic pagination, or a whole-source comparison. Research publisher cursors,
193
197
  revision identities, listings, corrections, and deletions, but use only deployed recipe grammar
@@ -9,6 +9,11 @@ mr-data checks RUN --json # every check the recipe declared
9
9
  mr-data receipt RUN --json # sources, digests, coverage, snapshot members
10
10
  ```
11
11
 
12
+ **The last of those answers `THIN_NO_MATCHING_ARTIFACT` on a run that sealed no receipt**, naming
13
+ what it sealed instead. That is the ordinary shape of a recipe with one `request`-windowed source
14
+ rather than a fault. Read `mr-data status RUN --json` for its coverage, as
15
+ [receipts](receipts.md) describes.
16
+
12
17
  **`peek` reports the logical type the run sealed.** The preview artifact carries column names
13
18
  alone, so the type comes from the run's own `column_profile`. A null `type` means the run sealed no
14
19
  profile for that column rather than that the column is untyped; check the run reached `persist`.
@@ -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 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 an exact single direct partition request-window plan with a compatible predecessor, or an all-recorded-stream plan. Closed-only, collection, mixed, 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. |
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. |
@@ -23,7 +23,7 @@ gap rather than doing anything, and `export-hosted-candidate`, which is a backen
23
23
  | `mr-data receipt` | Print what one run recorded about how it was built: every source it fetched and what those bytes hashed to, the coverage it delivered, and the members of its raw snapshot. |
24
24
  | `mr-data checks` | Report how each check the recipe declared came out — the name, whether it passed, what it found. Exits non-zero when one did not pass. |
25
25
  | `mr-data note` | Write one cell of a run's decision record — a heading, a body, and the typed blocks that carry a source, a decision or a clarification onto the stage its phase names. The body comes from `--markdown-file FILE`, which is the form to use; standard input is read when no file is named and is refused when it is a terminal or empty. `--list` reads back what is written. |
26
- | `mr-data download` | Bring one run's artifacts back to this computer, each one's bytes checked against the digest Studio sealed them under. `--kind` selects out of `table_parquet`, `table_manifest`, `table_part`, `column_profile`, `preview`, `receipt` and `raw_snapshot`; without it, the shape the run sealed — one file and the receipt for a table written whole, or the manifest, EVERY part the version names and the receipt for a table made of parts, written as `table/manifest.json` beside `table/parts/<part_key>.parquet`. A version composed over several refreshes names parts earlier runs sealed; those are fetched too, and a part that cannot be fetched refuses by name rather than leaving a folder holding part of a table. |
26
+ | `mr-data download` | Bring one run's artifacts back to this computer, each one's bytes checked against the digest Studio sealed them under. `--kind` selects out of `table_parquet`, `table_manifest`, `table_part`, `column_profile`, `preview`, `receipt` and `raw_snapshot`; without it, the shape the run sealed — one file and the receipt for a table written whole, or the manifest, EVERY part the version names and the receipt for a table made of parts, written as `table/manifest.json` beside `table/parts/<part_key>.parquet`. A version composed over several refreshes names parts earlier runs sealed; those are fetched too, and a part that cannot be fetched refuses by name rather than leaving a folder holding part of a table. A run that sealed no receipt brings back what it did seal, without one. |
27
27
  | `mr-data verify` | Hold one run's sealed table manifest against the parts it names, and report every disagreement. Without `--deep` it fetches the manifest alone; with `--deep` it fetches every part the run sealed and re-hashes it against the digest the manifest states. Exits non-zero when anything disagrees. |
28
28
  | `mr-data parts` | List one table version's parts — the identifier, the row count, the byte size and the recorded bounds of each — so a training loader can shard them across workers and read a table no single file should hold. `--version` names a version rather than the live one, and `--from`/`--to` keep only the parts a value range cannot rule out. Nothing is downloaded by listing them; the pattern is under [Reading a table that is made of parts](7-interrogate-ask-the-run-what-it-actually-delivered.md#reading-a-table-that-is-made-of-parts). |
29
29
  | `mr-data diff` | Compare two runs and say what changed. |
@@ -46,13 +46,41 @@ action, and blocking source names. It reads persisted facts; it does not authori
46
46
 
47
47
  A refresh executes only Studio's persisted source-action plan. The classifier may name closed
48
48
  reuse, a request window or collection delta, or recorded input, but current normal refresh
49
- materializes only an exact single direct partition request-window plan with a compatible
50
- predecessor, or an all-recorded-stream plan. Closed-only, collection, mixed, snapshot, and
51
- unwindowed plans return `RESYNC_REQUIRED` before acquisition. `mr-data table resync TABLE --request-id UUID` is the explicit
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
52
55
  full reread; retain and reuse the caller-generated UUID after a lost response. A held resync receipt
53
56
  prints `mr-data run --confirm-held RUN_ID` for that exact run. A sample-first resync instead uses
54
57
  `--approve-full` only after its preview succeeds.
55
58
 
59
+ **`--refresh` states its own range, and a run that starts one by hand must pass it.** `mr-data run
60
+ --refresh` sends what it is given. A refresh of a recipe carrying a non-snapshot request window
61
+ and no range is refused `RUN_WINDOW_REQUIRED` at the `transform` stage, before a source is fetched.
62
+ Write `--window START END`, two UTC timestamps, start included and end excluded. Do not subtract
63
+ the lookback yourself: each source's own `lookback_seconds` widens that start backwards and aligns
64
+ it down to a whole UTC day, floored at `start_at`. The lookback never reaches the end, though the
65
+ end is still aligned up to the next whole UTC day. `--backfill` takes
66
+ the same pair, and the client refuses a backfill without it before anything is sent. Nothing local
67
+ requires it on a refresh, so a refresh without it is accepted and dies in the run.
68
+
69
+ **A refresh asked for before the source's period closes is refused `WINDOW_NOT_READY`.** This
70
+ reaches only a source declaring `refresh_readiness`, which says the publisher serves a UTC day
71
+ once that day is over. Studio floors `now` minus the declared lag to the UTC day, and a refresh
72
+ whose range starts at or after that floor names no completed period. The run fails at once rather
73
+ than being held. A refresh asked for from the dashboard in that state is recorded as a noop with
74
+ the same code and starts no run. Only time clears it, and the refused run's range is frozen, so a
75
+ new run is needed once the day is over. Report the code and wait rather than retrying or revising
76
+ the recipe.
77
+
78
+ **A refresh on the one-windowed-source bounded route seals no `receipt`.** `mr-data receipt`
79
+ answers `THIN_NO_MATCHING_ARTIFACT` and names what the run did seal, which is the ordinary shape
80
+ of that route rather than a fault. Read `mr-data status RUN_ID --json` instead: its `coverage`
81
+ block carries `rows`, `bytes`, `window` and `truncated`, and on a one-source recipe that is that
82
+ source's own delivery.
83
+
56
84
  `auth`, `login`, `whoami` and `clarify` are the same implementation in both profiles, and
57
85
  `clarify` alone reaches nothing at all. The other twenty-eight answer from Studio. Twelve only
58
86
  read: `status`, `runs`, `watch`, `peek`, `receipt`, `checks`, `download`, `verify`, `parts`,
@@ -1,5 +1,32 @@
1
1
  ## Narrating the run
2
2
 
3
+ ### Content for the expanded activity pill
4
+
5
+ Give the reader a short heading and enough detail to understand the actual dataset. At meaningful
6
+ milestones, record what changed in their understanding:
7
+
8
+ - Sources: which publisher and format supply which fields, with observed coverage and limitations.
9
+ - Extraction: which fields were inspected and how they map to the requested columns.
10
+ - Cleaning and joins: exact matching keys, conversions, row grain and missing-value choices,
11
+ including why a choice preserves the requested meaning.
12
+ - Result: verified output rows and columns, checks and source coverage, retaining any truncation
13
+ qualification. A successful run alone does not prove the result meets the brief.
14
+
15
+ Use the recipe for planned semantics, worker updates for reported execution, and status, checks,
16
+ receipt and inspected output for completion claims. For example, before execution say “The recipe
17
+ matches observations by station and date”; say “Matching observations by station and date” only
18
+ when the worker reports that operation. A broad transform-stage update does not establish which
19
+ join is executing. Do not infer all sources were fetched, checks passed, or work finished from a
20
+ timer, progress count, quiet stream or agent activity. Worker progress is provisional reporting,
21
+ not verified result evidence. Keep names and numbers grounded in this dataset rather than copying
22
+ an example. Saving a table and making the dataset public are separate actions.
23
+
24
+ Use a small number of substantive notes, not a second event stream. Update a settled decision's
25
+ cell instead of repeatedly appending the same explanation. The UI renders worker progress itself;
26
+ agent prose supplies the source choices and reasoning that progress cannot explain.
27
+
28
+ ### Dataset and run records
29
+
3
30
  There are two decision records and they are not interchangeable. A cell is the same document in
4
31
  both: one outcome-led heading, a markdown body, and typed blocks the Dataset page lays out as rows
5
32
  a reader can compare. What differs is which log it lands in and when that log closes.
@@ -7,7 +34,7 @@ a reader can compare. What differs is which log it lands in and when that log cl
7
34
  The rule that binds them to the chat is in the
8
35
  [User communication contract](user-communication-contract.md#user-communication-contract): every message is a cell, in the same
9
36
  words, at the same moment — the message's first sentence is the `--heading` and the whole message
10
- is the body.
37
+ is the body. Factual page-only milestone cells are also useful; they do not require extra chat.
11
38
 
12
39
  **The DATASET's record is the primary one, and it never closes.**
13
40
  `mr-data dataset note DATASET_ID` writes it. The sources compared and refused, the grain argued
@@ -28,8 +55,10 @@ and what belongs there is what is true of that run alone: a clamp that truncated
28
55
  not pass, the exact revision this attempt tested. A run's narrative closes when the run reaches a
29
56
  terminal state, and an append after that answers `409 RUN_NARRATIVE_CLOSED` — a refusal no client
30
57
  check can soften, because only the backend knows the run has finished. A bounded run reaches its
31
- terminal state in seconds, so "while the run is going" means immediately after `mr-data run`
32
- returns the identifier, not after `status` says the run succeeded.
58
+ terminal state in seconds, so append run-specific notes while it is known to be active. If an
59
+ append races completion and receives `RUN_NARRATIVE_CLOSED`, write the useful finding to the
60
+ dataset record with the run identified in its context. Post-run checks, coverage and delivery
61
+ always belong in the dataset record; do not retry appending to a closed run.
33
62
 
34
63
  ```sh
35
64
  mr-data note --run RUN_ID --heading "The row ceiling stopped one source short" \
@@ -47,13 +47,16 @@ the cadence itself, and it belongs to the user.
47
47
  This call records a schedule; it is not proof of data continuation. Schedule only a current recipe
48
48
  that `mr-data recipe readiness --json` reports as refresh-ready. Studio must have a persisted bounded
49
49
  action for every source. The classifier may name closed reuse, a request-window or collection
50
- delta, or recorded stream input, but current normal refresh executes only an exact single direct
51
- partition request-window plan with a compatible predecessor, or an all-recorded-stream plan.
52
- Closed-only, collection, mixed, snapshot, and unwindowed plans return `RESYNC_REQUIRED` before
53
- acquisition. Use `mr-data table resync TABLE_ID --request-id UUID` when a full reread is
54
- genuinely needed; it is an explicit run, not a schedule fallback. Only returned state and durable
55
- run evidence support a claim that the table is caught up or refreshing. If that evidence is absent,
56
- report that a schedule was recorded and that ongoing freshness is unavailable.
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.
57
60
 
58
61
  `mr-data pin TABLE_ID --version VERSION_ID` freezes the pointer on an exact version for a rollback
59
62
  or a hold, `mr-data unpin TABLE_ID` resumes tracking, and `mr-data demote TABLE_ID` detaches the
@@ -23,6 +23,24 @@ A flag that cannot mean what it means locally is accepted and reported, never re
23
23
  digests, sources, timings and checks are already on the record it read. Read that key before
24
24
  repeating an argument that did nothing; only flags actually passed are reported.
25
25
 
26
+ ### Runs that seal no receipt
27
+
28
+ A recipe with exactly one source carrying a request window builds on a bounded route of its own,
29
+ and that route seals no `receipt` artifact. It is the route the first `full` build of such a recipe
30
+ takes, and every refresh after it takes the same one. `mr-data receipt` answers
31
+ `THIN_NO_MATCHING_ARTIFACT` and names what the run did seal, which is the raw source bundles, the
32
+ source state index, the source inventory, an incremental proof, the column profile, the preview,
33
+ the parts and the manifest. The run's own `receipt_digest` names that incremental proof rather than
34
+ a receipt. This is not a failure and not a deployment problem, so do not retry it and do not report
35
+ it as one.
36
+
37
+ What the run delivered is not lost with the receipt. `mr-data status RUN_ID --json` carries the
38
+ run record's `coverage` block: `rows`, `bytes`, `window` and `truncated`. Studio folds the
39
+ worker's per-source array into that one block, and on this route the recipe has exactly one
40
+ source, so the fold is that source's own numbers. `rows` is rows kept and `bytes` is bytes
41
+ fetched. The one field only a receipt carries is `rows_available`, so a run on this route cannot
42
+ say how many rows it left behind, only that `truncated` says there were some.
43
+
26
44
  ### Asking the table a question
27
45
 
28
46
  **In `mr-data query` the relation is always named `run_table`**, never the name the recipe gave the
@@ -8,7 +8,7 @@ cursors, digests, receipts, retry mechanics or service boundaries in anything th
8
8
  Where the host requires periodic status, that cadence is the only exception: one concise
9
9
  outcome-oriented sentence about the dataset stage or an observed result, inventing no progress.
10
10
 
11
- **Speak at six boundaries and nowhere else.** Each is a stage above, and each is one message:
11
+ **Send chat messages at these six boundaries.** Each is a stage above, and each is one message:
12
12
 
13
13
  | When | What it carries |
14
14
  | --- | --- |
@@ -28,9 +28,14 @@ message sent during a build is also a cell on the dataset's record: write it wit
28
28
  `mr-data dataset note`, taking the message's first sentence as the heading and the whole message as
29
29
  the body, in the same breath as sending it. The two are one act, not a message and a later summary
30
30
  of it. A person who joins by opening the page reads what the person in the chat read, and a person
31
- who scrolls the chat away still has it. The rule runs both ways: do not write a cell about work
32
- this document says to keep silent, and do not send a message you are not willing to write down. It
33
- licenses no extra update.
31
+ who scrolls the chat away still has it.
32
+
33
+ The expanded activity pill may also carry page-only milestone cells: selected sources and their
34
+ fields, a settled join or missing-value decision, a material finding, or a verified result. Write
35
+ one when the fact changes what a reader understands about the dataset; do not copy every worker
36
+ tick or routine command into a cell. These notes need no matching chat message. Preparation,
37
+ retries and transport mechanics remain silent on both surfaces. See
38
+ [build narration](narrating-the-run.md) for the distinction between planned and observed work.
34
39
 
35
40
  **Activity is not narration.** Setting the dataset's activity is a routine, silent act like any
36
41
  other command: never a chat message and never a cell. The rule above governs what is said;
@@ -28,8 +28,15 @@ themselves. Do not present a sequence of unlabeled paragraphs or default to a bu
28
28
 
29
29
  ### Agent presence lifecycle
30
30
 
31
- The floating pill represents only your active work. Start `dataset activity` immediately after creation, update the sentence when your task changes, and renew at least once a minute while you are actively working. The default lease is two minutes. Do not leave a detached heartbeat running after your turn ends. Worker ingestion progress has its own table surface and is not proof that you are working.
31
+ The floating pill combines agent activity with worker build progress. Your activity lease
32
+ represents only your active work. Start `dataset activity` immediately after creation, update the
33
+ sentence when your task changes, and renew at least once a minute while you are actively working.
34
+ The default lease is two minutes. Do not leave a detached heartbeat running after your turn ends.
35
+ Worker progress can continue in the expanded pill after your lease ends; it is not proof that you
36
+ are working. Do not renew your lease just to keep worker progress visible.
32
37
 
33
38
  When recovering, report what you are trying now; an earlier failed attempt belongs in the record. Before every final handoff, cancellation, or exhausted stop, report `--phase done` with a truthful final sentence (for example, “Dataset ready to explore” or “Stopped before the build completed”). Do this even when tables are not enabled. Only use `waiting_on_you` when a question is actually open — the brief at stage 2, the plan at stage 4, or the full build after a preview — and never under a delegation, which leaves nothing to wait on. A table going live does not finish your agent session.
34
39
 
35
- Stage 1 opened the dataset before research began; keep that same tab. Enable its optional Follow agent control for a watch-along experience; manual scrolling pauses it and must never be overridden.
40
+ Stage 1 opened the dataset before research began; keep that same tab. Follow agent starts enabled
41
+ for a watch-along experience; respect the viewer’s choice to pause or disable it. Manual scrolling
42
+ pauses following and must never be overridden.