mostlyright-data 0.29.9__tar.gz → 0.29.11__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 (124) hide show
  1. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/PKG-INFO +1 -1
  2. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/pyproject.toml +1 -1
  3. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/SKILL.md +74 -4
  4. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +70 -23
  5. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +17 -12
  6. mostlyright_data-0.29.11/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +16 -0
  7. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +6 -5
  8. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/commands.md +5 -5
  9. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/live-run.md +5 -3
  10. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/making-a-table-incremental.md +48 -10
  11. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/one-install.md +2 -2
  12. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/preparing-table-search.md +28 -2
  13. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/promote.md +4 -2
  14. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/readers.md +42 -13
  15. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/reference-pages.md +2 -2
  16. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/sources.md +94 -6
  17. mostlyright_data-0.29.11/src/mostlyright/data_harness/thin/fast_features.py +713 -0
  18. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/router.py +4 -1
  19. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/session.py +68 -5
  20. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4.py +2 -2
  21. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_public.py +38 -19
  22. mostlyright_data-0.29.9/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -11
  23. mostlyright_data-0.29.9/src/mostlyright/data_harness/thin/fast_features.py +0 -347
  24. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/.gitignore +0 -0
  25. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/README.md +0 -0
  26. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/scripts/hatch_build.py +0 -0
  27. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/agents/openai.yaml +0 -0
  28. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
  29. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
  30. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
  31. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
  32. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
  33. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/agent-protocol.md +0 -0
  34. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
  35. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
  36. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/boundaries.md +0 -0
  37. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
  38. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
  39. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/installation-parity.md +0 -0
  40. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/narrating-the-run.md +0 -0
  41. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
  42. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/prediction-labels.md +0 -0
  43. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/receipts.md +0 -0
  44. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
  45. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
  46. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/required-protocol.md +0 -0
  47. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/source-credentials.md +0 -0
  48. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
  49. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/transforms.md +0 -0
  50. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/user-communication-contract.md +0 -0
  51. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
  52. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
  53. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/__init__.py +0 -0
  54. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/agent_protocol.py +0 -0
  55. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/canonical.py +0 -0
  56. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/formats.py +0 -0
  57. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
  58. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/key_seam.py +0 -0
  59. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/page_coverage.py +0 -0
  60. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
  61. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/session_probes.py +0 -0
  62. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/skill_assets.py +0 -0
  63. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/table_manifest.py +0 -0
  64. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/__init__.py +0 -0
  65. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/acquire.py +0 -0
  66. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
  67. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/activity.py +0 -0
  68. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/approvals.py +0 -0
  69. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/categories.py +0 -0
  70. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/commands.py +0 -0
  71. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
  72. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/download.py +0 -0
  73. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/narrative.py +0 -0
  74. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/parity.py +0 -0
  75. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/probe.py +0 -0
  76. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/progress_rate.py +0 -0
  77. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
  78. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/propose.py +0 -0
  79. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/recipe.py +0 -0
  80. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
  81. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/recipe_explain.py +0 -0
  82. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/recipe_forecast.py +0 -0
  83. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
  84. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/research.py +0 -0
  85. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/run_diagnose.py +0 -0
  86. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/runs.py +0 -0
  87. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/stream.py +0 -0
  88. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
  89. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/studio_capabilities.py +0 -0
  90. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/transport.py +0 -0
  91. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
  92. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
  93. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
  94. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_connected.py +0 -0
  95. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
  96. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
  97. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_datasets.py +0 -0
  98. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
  99. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
  100. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
  101. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
  102. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
  103. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_search.py +0 -0
  104. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
  105. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
  106. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
  107. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
  108. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/__init__.py +0 -0
  109. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/attendance.py +0 -0
  110. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/clarification.py +0 -0
  111. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
  112. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
  113. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
  114. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
  115. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
  116. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
  117. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
  118. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
  119. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/credentials.py +0 -0
  120. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/login.py +0 -0
  121. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
  122. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
  123. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/src/mostlyright/data_harness/ux/remediation.py +0 -0
  124. {mostlyright_data-0.29.9 → mostlyright_data-0.29.11}/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.29.9
3
+ Version: 0.29.11
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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mostlyright-data"
3
- version = "0.29.9"
3
+ version = "0.29.11"
4
4
  description = "Mostly Right hosted CLI for reviewed datasets"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -9,6 +9,67 @@ Build the dataset the user requested, verify its contents, and report its actual
9
9
  Use hosted commands with `--json` and returned identifiers. This core workflow governs the
10
10
  references. Load the reference for the current stage only, not the entire library before starting.
11
11
 
12
+ ## Four gates
13
+
14
+ Every build passes these four gates in order. Do not skip one. The numbered steps below carry
15
+ the detail.
16
+
17
+ **Gate 1. Read what Studio serves and what the recipe misses.** Draft the recipe. Run
18
+ `mr-data recipe explain RECIPE.json --json` before you register. Read `studio_capabilities`: a
19
+ capability that is not literal `true` is unavailable on this Studio. Read `fast_features`. On a
20
+ new table, fix every entry marked `missed`, or say in the build message why it stays.
21
+ On a table that is already live, list the missed entries and ask the user first. Each fix changes
22
+ the recipe digest, so the next build is a full resync. A paid source is called again for every
23
+ per-row request whose saved answer no longer applies.
24
+
25
+ **Gate 2. State a pace and leave concurrency out.** A page collection that states no pace sends
26
+ one request a second. State `pages.min_interval_milliseconds` as the gap the publisher documents.
27
+ No documented limit: state 1000 ms and say so. That is a valid reason. Registration refuses a gap
28
+ under 16 ms. That is a platform bound, never a target. Where the deployment spreads requests over
29
+ its egress addresses, the gap is kept per address, so the publisher can see a multiple of the
30
+ stated rate. No command prints how many addresses there are. So when the publisher's limit is
31
+ per key or per account, state 1000 ms unless the user confirms the deployment does not spread
32
+ requests. Leave `pages.concurrency` out: the worker then sizes the width, and a stated value is
33
+ only a ceiling. Keep a stated one only when a run's refusal told you to lower it or the publisher
34
+ documents a limit on concurrent connections, and record the reason in a dataset note so the next
35
+ session finds it. Then choose the source shape that fits.
36
+
37
+ | Source shape | Use | Always state |
38
+ | --- | --- | --- |
39
+ | One public file that never changes | `public.https` with a Reader pin | `closed: true` |
40
+ | One large public Parquet file whose publisher answers byte ranges | a `parquet.tabular@1.1.0` pin | `columns` |
41
+ | Keyed JSON API paged by page number, whose first page states the page count | `authenticated.https.api_key@2.1.0` with the counted `pagination.parallel` | `total_pages_pointer`, `max_page_bytes`, the gap |
42
+ | API that takes a date range | `window.request` | `lookback_seconds` |
43
+ | Many pages behind one index | `public.https.collection@2.0.0` | the gap |
44
+ | One request per row of another source | a per-row `request`, with `batch` when a `GET` takes a list of ids | the gap |
45
+ | Bulk dump beside a windowed source | `reference` | `recheck_seconds` |
46
+ | Live `wss://` feed | a recorded stream | see [Streams](references/recording-a-stream-venue.md) |
47
+
48
+ [Sources](references/sources.md#pace-and-width) has the pace rules and the limits of parallel
49
+ pages. [Readers](references/readers.md) has the Reader pins and the limits of a ranged read.
50
+
51
+ **Gate 3. Run a bounded test on a scratch table.** Never test on the real table: its first
52
+ succeeded run goes live, truncated or not. For a public CSV that needs no key, run
53
+ `mr-data source inspect`. For anything else follow [the probe rule](references/3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table): copy the drafted
54
+ recipe under another `table.name`, drop `table.search` from the copy, write small budgets into
55
+ it, and take one bounded run.
56
+ A sample's `--max-rows` cuts rows after the fetch. It does not shorten a collection walk, so the
57
+ scratch recipe states a small `pages.max_fetches_per_run`. A per-row request collection also
58
+ states `pages.stop_at_budget: true`, or it fails at that budget. A source whose provider bills
59
+ per call spends real calls on the test, and the real table cannot reuse them. Forecast it and get
60
+ the user's decision before the test. Retire the scratch table with `mr-data demote TABLE_ID` and
61
+ then `mr-data table archive TABLE_ID --confirm DELETE`.
62
+
63
+ **Gate 4. Forecast, then one full run.** Run
64
+ `mr-data recipe forecast RECIPE_ID --digest DIGEST --json`. Exit 3 means resize before you run.
65
+ Read the registration `warnings`. Then run `--full` once and follow it with `mr-data watch`.
66
+
67
+ The steps below are numbered 1 to 10. The reference files are numbered by stage, 1 to 9. Steps 1
68
+ and 2 are stage 1. Step 3 is stage 2. Step 4 is stage 3. Step 5 is stages 4 and 5. Steps 6 and 7
69
+ are stage 6. Step 8 is stage 7. Step 9 is stage 8. Step 10 is stage 9. Gates 1 and 2 belong to
70
+ step 5, where the recipe is drafted. Gate 3 needs that draft, so it runs after step 5 and before
71
+ step 6. The stage 3 reference holds its rule. Gate 4 is step 6.
72
+
12
73
  ## Start and agree the brief
13
74
 
14
75
  1. Run `mr-data whoami --json` once. If authentication needs repair, read
@@ -60,12 +121,20 @@ references. Load the reference for the current stage only, not the entire librar
60
121
  Decide how each source continues before you register it, and whether the table needs a
61
122
  declared `rebuild`: read [making a table incremental](references/making-a-table-incremental.md).
62
123
  Run `mr-data recipe explain RECIPE.json --json` first. It names every reason the table could
63
- not refresh, not only the first. Its `fast_features` names each fast feature the recipe
64
- misses, with a one-line fix. Write `table.partition` for any table no window partitions that
124
+ not refresh, not only the first. Its `fast_features` has eight entries, and each missed one
125
+ carries a one-line fix. Write `table.partition` for any table no window partitions that
65
126
  has a date-like column, `batch` for a per-row GET whose API takes a list of ids, and
66
127
  `reference` for a bulk dump beside a window
67
128
  ([fast builds](references/making-a-table-incremental.md#fast-builds-tablepartition-batch-and-reference)).
68
- Put them in the first revision, because each later change costs one resync. Adopting
129
+ State the gap, drop a stated `pages.concurrency`, page a keyed API in parallel and pin the
130
+ ranged Parquet Reader where the entries `pace`, `concurrency`, `parallel_pagination` and
131
+ `ranged_parquet` say so and the source meets each condition
132
+ ([fetching fast](references/making-a-table-incremental.md#fetching-fast-pace-width-parallel-pages-and-ranged-parquet)).
133
+ Put them in the first revision. Registering a revision is free, but building one is a full
134
+ resync, so each later change costs one resync.
135
+ On a table that is already live, list the missed entries and ask the user first. Each fix
136
+ changes the recipe digest, so the next build is a full resync. A paid source is called again
137
+ for every per-row request whose saved answer no longer applies. Adopting
69
138
  `table.partition` on an existing table costs one full resync, so for a paid source ask the
70
139
  user before that resync, as the brief and forecast rules require. Before any
71
140
  `--full` of a paid collection, run `mr-data recipe forecast RECIPE_ID --digest DIGEST --json`
@@ -75,7 +144,8 @@ references. Load the reference for the current stage only, not the entire librar
75
144
  Use `--full` for the requested build: it is progressive by default, with a five-minute
76
145
  inspection checkpoint while the same acquisition continues. Choose bounds from measured
77
146
  source size; row and byte clamps apply per source, not elapsed time. Use explicit `--sample`
78
- only for a deliberate standalone inspection. Preserve the agreed semantics and quality checks.
147
+ only for a bounded test on a scratch table, as gate 3 says. Preserve the agreed semantics and
148
+ quality checks.
79
149
  7. Follow the returned run with `mr-data watch RUN_ID --json`. Its `source_rates` and
80
150
  `mr-data status RUN_ID --json` on a live run give each collection source's rate and time left.
81
151
  Quote those numbers rather than doing the arithmetic. Read `run.status`: returning an existing
@@ -79,11 +79,12 @@ refusal to the provider and never bypass it.
79
79
  reason codes, only once the discovery lanes and the comparisons are complete — and claim
80
80
  `unsupported` only after every typed-refusal chain ends `lawful_routes_exhausted`.
81
81
 
82
- **Reading the bytes is a bounded run, not `mr-data probe`.** `probe SOURCE_ID QUESTION_ID
82
+ **Reading the bytes is a bounded test run, not `mr-data probe`.** `probe SOURCE_ID QUESTION_ID
83
83
  --dataset DATASET_ID` takes two identifiers rather than a question in words, and no v4 registration
84
84
  receipt returns a question identifier; it also rides the frozen `/v3/sessions` routes, which a
85
- deployment may have switched off. So the reading path on a hosted workspace is the ordinary loop,
86
- run early: register the recipe and take one run without `--window`.
85
+ deployment may have switched off. So the reading path on a hosted workspace is
86
+ [the probe rule](#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table) below: one
87
+ bounded run of a scratch table.
87
88
 
88
89
  **A public CSV can also be looked at before anything is registered.** `mr-data source inspect` reads
89
90
  the start of one HTTPS file that needs no key and answers with what it saw, with no recipe and no
@@ -107,7 +108,7 @@ start of the file and not the file. Its `evidence` carries the `format`, the `co
107
108
  not an answer yet, so read it back again later. A status of `truncated` is an answer that stopped at
108
109
  a limit: keep its limits and its evidence with whatever you record, and never call it complete. The
109
110
  command takes no key and no Reader options, so a source that needs either, or that is not a CSV, is
110
- read by the run below instead. So is any source that answers `capability_unavailable`, which means
111
+ read by the test run below instead. So is any source that answers `capability_unavailable`, which means
111
112
  this deployment cannot inspect it: do not retry it, and do not guess in its place.
112
113
 
113
114
  **Write down what each look found.** An inspection is not a run, and nothing on the page shows what
@@ -128,33 +129,79 @@ observed_at, stage_m and quality_flag.
128
129
  EOF
129
130
  ```
130
131
 
131
- **Both obey the ordinary gate.** Registering needs the brief answered or delegated, so the run
132
- happens AFTER stage 2's answers arrive — or at once under a delegation — and an inspection, which
133
- writes a question and a source onto the dataset, waits for the same answers. What continues while
132
+ **Both obey the ordinary gate.** Registering needs the brief answered or delegated. So the test
133
+ run happens after stage 2's answers arrive, or at once under a delegation. An inspection writes a
134
+ question and a source onto the dataset, so it waits for the same answers. What continues while
134
135
  you wait is research that submits nothing: documentation, publisher comparison, catalog.
135
136
 
137
+ #### The probe rule: test on a scratch table, never on the real table
138
+
139
+ Test a recipe on a scratch table with small budgets. Never test on the real table. A table's
140
+ first succeeded run goes live, truncated or not. A test run on the real table would serve a cut
141
+ table on the dataset page. This stage starts no full build of the real table. Stage 6 starts
142
+ that one build. The test needs a drafted recipe, so run it once stage 5 has a draft and before
143
+ stage 6.
144
+
145
+ 1. Copy the recipe document. Give the copy its own `table.name`. Keep the same `dataset.id`.
146
+ Drop `table.search` from the copy, so the test prepares no search index.
147
+ 2. Write small budgets into the copy, as the list below says.
148
+ 3. Register the copy with `mr-data recipe SCRATCH.json --json`. It returns a recipe id, a digest
149
+ and a table id of its own.
150
+ 4. Take one bounded run and read it back.
151
+ 5. Retire the scratch table. A scratch table goes live too, so withdraw it first.
152
+
136
153
  ```sh
137
- mr-data run --recipe RECIPE_ID --digest RECIPE_DIGEST --full --json
138
- mr-data status RUN --inspection --json # inspect while the same full acquisition continues
154
+ mr-data run --recipe SCRATCH_RECIPE_ID --digest SCRATCH_DIGEST --sample --max-rows 2000 --json
139
155
  mr-data peek RUN --json # the columns and types the run sealed
140
156
  mr-data query RUN "select count(*) from run_table" --json
141
157
  mr-data receipt RUN --json # what was fetched and what those bytes hashed to
158
+ mr-data status RUN --diagnose --json # progress, plan counts and how each attempt ended
159
+ mr-data demote SCRATCH_TABLE_ID --json
160
+ mr-data table archive SCRATCH_TABLE_ID --confirm DELETE --json
142
161
  ```
143
162
 
144
- **Inspect the full build while it runs.** Its progressive checkpoint appears after about five
145
- minutes without stopping acquisition. Use an explicit `--sample` only for a deliberate standalone
146
- experiment: a row ceiling can truncate output after every collection page was fetched and does
147
- not promise a short acquisition. Never derive source size from a truncated sample's ceiling.
148
-
149
- **A reading run that came back whole IS the build, so stage 6 adopts it rather than repeating
150
- it.** Untruncated everywhere is the build, whichever stage started the run: keep this `run_id`.
151
- The run meets Studio's ordinary size confirmation before acquisition when it is large (a size
152
- check, not a charge); its checkpoint requires no
153
- second approval or second full build.
154
-
155
- That is stages 5 through 7 run once to settle the shape. Where the deployment does answer a source
156
- inspection or a probe, keep its status, reason code and exit value internal and describe only what
157
- it settled.
163
+ **Know what a sample bounds and what it does not.**
164
+
165
+ - `--max-rows` cuts each source's rows after that source is fetched and decoded. It does not
166
+ shorten a collection walk. Every page the budgets admit is still fetched.
167
+ - A collection's walk is bounded by `pages.max_fetches_per_run` and `discovery.max_requests`.
168
+ Write both small in the scratch recipe. A listing collection that spends its fetch budget
169
+ still succeeds and reports `complete: false`.
170
+ - A per-row request collection fails with `ROW_REQUESTS_EXCEED_BUDGET` at its fetch budget.
171
+ State `pages.stop_at_budget: true` beside the small budget in the scratch recipe. The run
172
+ then seals the rows it answered and reports `complete: false`.
173
+ - A row clamp does not truncate a per-row request collection. Answers past the clamp fail the
174
+ run with `ROW_REQUESTS_EXCEED_BUDGET`. Keep `--max-rows` above the rows its budget can answer.
175
+ - A document projection source cannot run under a row clamp. The run fails with
176
+ `CLAMP_UNSUPPORTED_FORMAT`. Test it with a full run of the scratch table:
177
+ `mr-data run --recipe SCRATCH_RECIPE_ID --digest SCRATCH_DIGEST --full --json`.
178
+ - A source with a request window is read for its latest `lookback_seconds` only.
179
+ [Stage 6](6-build-one-run-sized-to-acquire-every-measured-source-whole.md) has the exact days.
180
+ - `--max-source-bytes` and `limits.max_source_bytes` refuse in every mode. They never truncate.
181
+ - A sample must state a clamp. Studio refuses a sample with none as `CLAMPS_REQUIRED`. This
182
+ client refuses it first as `THIN_REQUEST_INVALID`. Give `--max-rows`, `--max-source-bytes` or
183
+ `--window`.
184
+
185
+ **A paid source spends real calls on the test.** A per-row request whose provider bills per call
186
+ makes a paid call for every request the scratch run sends. The real table cannot reuse those answers,
187
+ because saved answers belong to the table that holds them. Run `mr-data recipe forecast` on the
188
+ scratch recipe and get the user's decision before the test.
189
+
190
+ **Retire a scratch table by what happened to it.** A scratch table whose run succeeded is live:
191
+ `demote` it, then `table archive` it. One whose run failed never went live, and `demote` answers
192
+ `TABLE_NOT_PROMOTED` for it. Go straight to `table archive`. To test a revision, register it
193
+ under the same scratch `table.name` before you archive. After the archive the name is free
194
+ again, and registering it makes a new table.
195
+
196
+ **Keep the activity line truthful during the test.** The page shows the scratch table going live
197
+ as a table event. That event does not write your activity line. Say in the line that you are
198
+ testing on a scratch table, and do not report `--phase done` for it.
199
+
200
+ Never derive a source's size from a truncated sample's ceiling. A check on the whole table, such
201
+ as a row floor, can fail on a sample of a sound recipe. Read the registration `warnings` first.
202
+
203
+ Where the deployment does answer a source inspection or a probe, keep its status, reason code and
204
+ exit value internal and describe only what it settled.
158
205
 
159
206
  **An acquisition's answer is evidence, and cheap to reuse.** Acquisitions are cacheable by
160
207
  source, query digest and window, so a fetch feeds a later build in the same warm session without
@@ -1,13 +1,17 @@
1
1
  ### 6. Build — one run, sized to acquire every measured source whole
2
2
 
3
- **Stage 3's reading run is usually already this run.** Where it came back with
4
- `coverage.truncated` false everywhere, that run is the build: keep it, present it at stage 7, start
5
- nothing here. Re-running the coordinate replays it; a different ceiling buys only another
6
- acquisition.
7
-
8
- **Otherwise start one progressive full build.** Use the registered recipe and measured bounds
3
+ **Stage 3 tested the recipe on a scratch table. This stage starts the one full build of the real
4
+ table.** Do not test on the real table: its first succeeded run goes live, truncated or not.
5
+ [The probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table)
6
+ has the scratch-table steps. Before the full build, run
7
+ `mr-data recipe forecast RECIPE_ID --digest RECIPE_DIGEST --json`. Exit 3 means the run as declared
8
+ would outlast one attempt or overrun a quota, so resize it first. Read the registration
9
+ `warnings` too.
10
+
11
+ **Then start one progressive full build.** Use the registered recipe and measured bounds
9
12
  for the requested coverage. Every full run is progressive by default; agents do not choose
10
- between progressive and sample-first acquisition.
13
+ between progressive and sample-first acquisition. Re-running the same coordinate replays its
14
+ stored result. A different ceiling buys only another acquisition.
11
15
 
12
16
  ```sh
13
17
  mr-data run --recipe RECIPE_ID --digest RECIPE_DIGEST --full --json
@@ -19,11 +23,12 @@ limit. A short build can finish before a checkpoint is needed. Do not submit ano
19
23
  inspecting it. A required size confirmation happens before acquisition; it is not a charge. The
20
24
  older `--progressive` flag is accepted for compatibility but is unnecessary.
21
25
 
22
- **A standalone sample is an explicit inspection choice.** Use `--sample --max-rows 2000` only
23
- when a separate bounded experiment is needed. Rows are clamped after acquisition, so a collection
24
- may still fetch every page and take longer than five minutes. For an already authorized full
25
- build, use the progressive checkpoint. A truncated sample is a preview: report its actual
26
- coverage and the full build's proposed coverage before requesting any missing authorization.
26
+ **A standalone sample belongs on a scratch table.** Use `--sample --max-rows 2000` only for a
27
+ separate bounded experiment, and run it as the probe rule says. Rows are clamped after
28
+ acquisition, so a collection still fetches every page its budgets admit and can take longer than
29
+ five minutes. For an already authorized full build, use the progressive checkpoint. A truncated
30
+ sample is a preview: report its actual coverage and the full build's proposed coverage before
31
+ requesting any missing authorization.
27
32
 
28
33
  > The preview stopped at 2,000 rows on `asos_observations`, which is the ceiling rather than the
29
34
  > end of the feed; the other three sources came back whole. The full build would cover 2000 to 2026
@@ -0,0 +1,16 @@
1
+ ### 8. Fix — revise the document and register it again
2
+
3
+ Change the document and send it again. Same dataset, same table, new digest:
4
+
5
+ ```sh
6
+ mr-data recipe RECIPE.json --json
7
+ ```
8
+
9
+ **Registering a revision is free and abandons nothing. Building it costs one resync.**
10
+ Registration starts no run. Table identity is stable across revisions: no `_v2.._vN` renaming, no
11
+ proposal wedged on an id somebody has to reclaim, no cleanup after a failed run. The revision has
12
+ a new digest, so the sealed predecessor no longer matches it. Its next build reads every source
13
+ again. A per-row request collection still reuses the answers the live version holds while its
14
+ request, Reader and binding stay the same. Put every change in one revision so the table pays
15
+ once. Forecast a source whose provider bills per call, and ask the user before that resync.
16
+ Build again, interrogate again, until the checks and coverage say what you need.
@@ -12,9 +12,9 @@ you did not read off `coverage`.
12
12
  every coverage entry, nothing was cut and the table this run sealed is the table a full run would
13
13
  have sealed. When it is true anywhere, what you have is a shape rather than an answer.
14
14
 
15
- **Untruncated everywhere is the build. Present it as one.** The mode a run started in is not what
16
- makes a table finished, and calling a complete table "a preview" tells somebody their dataset does
17
- not exist yet when it does. Say what was built, and link the page:
15
+ **The real table's full run, untruncated everywhere, is the build. Present it as one.** A
16
+ scratch-table test is never the build, whatever its coverage says. Calling a complete table "a
17
+ preview" tells somebody their dataset does not exist yet when it does. Say what was built, and link the page:
18
18
 
19
19
  > Built 43,812 rows from four sources, every source acquired whole and nothing truncated. All four
20
20
  > declared checks passed. Live at <link>. It refreshes every 6 hours, because the publisher posts
@@ -39,8 +39,9 @@ refresh delegation described above. Stage 6 carries the run commands.
39
39
  the dataset page, so before acting on this recipe again read `mr-data runs --json --mode full` and,
40
40
  for any run newer than the preview that you did not start, `mr-data status RUN_ID --json` to see
41
41
  whether it names this recipe and digest. One that does is theirs, and its state says what to do.
42
- `queued`, `running` or `succeeded` is the full build: adopt it as if you had started it — write its
43
- run cell, `mr-data watch RUN_ID --json` if it has not ended, then stage 7. `awaiting_confirmation`
42
+ `queued`, `running` or `succeeded` is the full build of the real table: adopt it as if you had
43
+ started it. Write its run cell, `mr-data watch RUN_ID --json` if it has not ended, then stage 7. A
44
+ run of a scratch table is never adopted as the build. `awaiting_confirmation`
44
45
  is the size gate (not a charge), which the same command settles with `--confirm`.
45
46
  `awaiting_sample_approval` is stage 6's held full: read its `sample_run_id`, interrogate that
46
47
  preview, and release it with `mr-data run --approve-full`. `failed` is the full build too: read its
@@ -9,16 +9,16 @@ gap rather than doing anything, and `export-hosted-candidate`, which is a backen
9
9
  | `mr-data login` | Complete device approval and store a device credential. `login --force` is the compatibility alias for safe `auth rotate`, never an in-place truncate. |
10
10
  | `mr-data whoami` | Use the compatibility alias for the remotely validated `auth status` answer. |
11
11
  | `mr-data clarify` | Check whether clarification is allowed. Use `--attended` for an active user chat and `--unattended` for a scheduled invocation; an explicit operator veto such as `MOSTLYRIGHT_ATTENDED=0` still wins. A non-TTY subprocess does not prove the user is absent. The command does not ask the person in chat or wait for a reply; the agent must do that. It exits non-zero when nobody can answer or the recipe is already registered. |
12
- | `mr-data probe` | `probe SOURCE_ID QUESTION_ID --dataset DATASET_ID [--kind source_inspect\|sample_rows\|profile_columns\|evaluate_expression]` asks one already-registered source one question and prints the answer. **It rides the frozen `/v3/sessions` routes, which a deployment may have switched off, so do not plan a source inspection around it.** Both positionals are identifiers — `QUESTION_ID` is a question identifier, which no v4 registration receipt returns. The v4 way to look at a source before registering it is `mr-data source inspect`, in the next row; the other way to read one is to register the recipe and take one run without `--window` under `--max-rows`, then `peek`, `query` and `receipt`. |
13
- | `mr-data source` | `source inspect` takes a bounded look at one source, and it is the v4 path where `probe` is not; [stage 3](3-probe-read-a-source-before-committing-to-it.md) says when to use it. It starts one of three ways, and each prints the `session_id`, `probe_id` and `source_id`, plus the `dataset_id` and `question_id` when it has them. For a new public HTTPS CSV that needs no key, give `--dataset-id`, `--question`, `--source-name`, `--source-url`, `--claimed-basis` and `--claim-evidence-digest`: it prepares the source and opens a session. For a source already prepared, give `--dataset-id`, `--question-id` and `--source-id`, which opens a session. In a session you already hold, give `--session-id` with `--source-id`. `--context-source-id` adds a source to a session it opens. `--session-id` with `--probe-id` reads the answer back. A look is bounded to 65,536 bytes, 1,000 rows and 300 seconds; `queued` and `truncated` are not complete answers, and `capability_unavailable` means this deployment cannot inspect, so read the source by a bounded run instead. What it finds is exploratory evidence, not a run and not a receipt. |
14
- | `mr-data reader` | `reader normalize --family-id ID --family-version VERSION --options-file FILE [--workspace-id ID]` asks Studio's Reader service to apply one certified family's own defaults and option rules to a JSON object of options, at most 64 KiB, and prints the normalized `decode_options` and `decode_options_json`. The second is the text a recipe carries as `reader.decode_options`, pinned as [Readers](readers.md) describes. It changes nothing, and this client does not validate or invent a family's options: Studio decides. |
12
+ | `mr-data probe` | `probe SOURCE_ID QUESTION_ID --dataset DATASET_ID [--kind source_inspect\|sample_rows\|profile_columns\|evaluate_expression]` asks one already-registered source one question and prints the answer. **It rides the frozen `/v3/sessions` routes, which a deployment may have switched off, so do not plan a source inspection around it.** Both positionals are identifiers. `QUESTION_ID` is a question identifier, which no v4 registration receipt returns. The v4 way to look at a source before registering it is `mr-data source inspect`, in the next row. The other way to read one is a bounded run of a scratch table, never of the real table, as [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table) says. |
13
+ | `mr-data source` | `source inspect` takes a bounded look at one source, and it is the v4 path where `probe` is not. [Stage 3](3-probe-read-a-source-before-committing-to-it.md) says when to use it. It starts one of three ways, and each prints the `session_id`, `probe_id` and `source_id`, plus the `dataset_id` and `question_id` when it has them. For a new public HTTPS CSV that needs no key, give `--dataset-id`, `--question`, `--source-name`, `--source-url`, `--claimed-basis` and `--claim-evidence-digest`: it prepares the source and opens a session. For a source already prepared, give `--dataset-id`, `--question-id` and `--source-id`, which opens a session. In a session you already hold, give `--session-id` with `--source-id`. `--context-source-id` adds a source to a session it opens. `--session-id` with `--probe-id` reads the answer back. A look is bounded to 65,536 bytes, 1,000 rows and 300 seconds. `queued` and `truncated` are not complete answers, and `capability_unavailable` means this deployment cannot inspect, so read the source by a bounded run of a scratch table instead, as [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table) says. What it finds is exploratory evidence, not a run and not a receipt. |
14
+ | `mr-data reader` | `reader normalize --family-id ID --family-version VERSION --options-file FILE [--workspace-id ID]` asks Studio's Reader service to apply one certified family's own defaults and option rules to a JSON object of options, at most 64 KiB, and prints the normalized `decode_options` and `decode_options_json`. The second is the text a recipe carries as `reader.decode_options`, pinned as [Readers](readers.md) describes. It changes nothing, and this client does not validate or invent a family's options: Studio decides. Run it for every Reader pin before you register, so a wrong option is refused here and not inside a run. |
15
15
  | `mr-data catalog` | `catalog search "QUESTION"` asks the sealed public-source catalogue which of its entries might answer a question and ranks them best first. Every ranked entry comes back with the disposition its own facts earned — `admitted`, `human_escalation_required` or `refused` — because an entry the catalogue could not vouch for is still a finding. `--format csv` states the one data format the question requires — one token per question, never repeated — and it is not a filter: an entry that does not declare that format still comes back ranked, with disposition `refused` and `filters_match` false, so nothing is held back; `--limit N` ranks at most N, up to 25. It fetches nothing and registers nothing. The catalogue holds one provider (Data.gov) and only part of it, so it is never exhaustive and an empty answer is not evidence that no such source exists. When the answer is `catalog_unavailable`, this deployment has no catalogue to search: record the lane and carry on. |
16
16
  | `mr-data public` | Read Mostly Right's public dataset catalogue. `public search "QUERY"` ranks published datasets (`--topic`, `--limit` and `--cursor` narrow and page it), `show SLUG` reads one overview, `tables SLUG` lists its table identifiers, `schema TABLE_ID` reads one table's schema, `sample TABLE_ID` prints its published preview rows, `lookup "QUERY"` is the connector search shape and `fetch SLUG` returns the dataset's Markdown document. Those seven need no login. `query TABLE_ID`, with `--body` or `--body-file`, and `download-url TABLE_ID` do need `mr-data login`: they ask Cloud, with the device key, to run one query over a public table and to say how it can be downloaded. Preview rows are not a query, and no table bytes pass through the command. |
17
17
  | `mr-data connected` | `connected list` shows the public datasets connected to this workspace and `connected add SLUG` connects one by its slug. It needs `mr-data login`: both ask Cloud, with the device key, over the routes MCP's `list_connected_datasets` and `connect_dataset` use. |
18
18
  | `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`, unless the user's own request already has an empty dataset open, in which case it answers `open_request_reused` with that `dataset_id` and mints nothing (`--separate` mints a new one for a different dataset); `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; send `--name` and `--topics` whenever you send `--description-file`, because a description sent on its own can make Studio write a new title and new tags, and read `changed` and `written_by_studio` on the receipt for every field the write moved; 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. |
19
- | `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. `mr-data recipe explain FILE` (or a recipe identifier) lists every reason its table can or cannot refresh, each with a rule code and Studio's suggested repair, and exits 2 when one blocks it. `mr-data recipe forecast RECIPE_ID --digest DIGEST` says what one run would ask of each source before it runs, and exits 3 when the run would overrun today's platform quota or one attempt. `mr-data recipe readiness --table TABLE_ID --current-only` reads the revision one table runs now. [Making a table incremental](making-a-table-incremental.md#the-loop-explain-forecast-run-diagnose) says when to use each. |
19
+ | `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. `mr-data recipe explain FILE` (or a recipe identifier) lists every reason its table can or cannot refresh, each with a rule code and Studio's suggested repair, and exits 2 when one blocks it. `mr-data recipe forecast RECIPE_ID --digest DIGEST` says what one run would ask of each source before it runs, and exits 3 when the run would overrun today's platform quota or one attempt. `mr-data recipe readiness --table TABLE_ID --current-only` reads the revision one table runs now. The explanation's `fast_features` names each fast feature the recipe uses or misses, with a one-line fix. [Making a table incremental](making-a-table-incremental.md#the-loop-explain-forecast-run-diagnose) says when to use each. |
20
20
  | `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. |
21
- | `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 source-action plan, and a plan shape it does not refresh returns `RESYNC_REQUIRED` before acquisition; [which plans refresh](making-a-table-incremental.md#which-plans-refresh) lists them, and `mr-data recipe explain` says which one a table is. 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 size confirmation (not a charge), which `--confirm` settles and whose printed `confirm_command` re-states every argument the request carried. `--approve-full RUN_ID` releases the full an existing legacy 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 and ceilings and naming the failed run so Studio starts a new attempt, and refuses with the triple when the recipe was at fault. `--cancel RUN` stops one. |
21
+ | `mr-data run` | Start one run against a registered recipe, named as `--recipe RECIPE_ID --digest RECIPE_DIGEST`. Both are required, neither is 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 source-action plan, and a plan shape it does not refresh returns `RESYNC_REQUIRED` before acquisition. [Which plans refresh](making-a-table-incremental.md#which-plans-refresh) lists them, and `mr-data recipe explain` says which one a table is. 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. `--max-rows` cuts rows after a source is fetched and decoded, so it does not shorten a collection walk. A sample with no clamp is refused: `CLAMPS_REQUIRED` from Studio, and `THIN_REQUEST_INVALID` from this client first. A table's first succeeded run goes live, truncated or not, so test on a scratch table as [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table) says. A large run is held for a size confirmation (not a charge), which `--confirm` settles and whose printed `confirm_command` re-states every argument the request carried. `--approve-full RUN_ID` releases the full an existing legacy 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 and ceilings and naming the failed run so Studio starts a new attempt, and refuses with the triple when the recipe was at fault. `--cancel RUN` stops one. |
22
22
  | `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. `--diagnose` adds everything else Studio can say about the run: its progress, a per-row source's plan counts, its checkpoints, and how each attempt ended, with a lost attempt's cause marked as an inference. |
23
23
  | `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. `--dataset DATASET_ID` narrows it to one dataset's runs, so "what have I run for this dataset?" is one scoped read, and a value that is not an identifier is refused `THIN_ARGUMENT_INVALID`. `--status` and `--mode` narrow it too and are refused `THIN_ARGUMENT_INVALID` for a word outside the seven states or the six modes; the three combine. Studio narrows before it cuts a page, so a filtered page holds the newest runs that match however many runs the workspace has. `--limit` says how many, and pages are followed to reach it; a listing that does not end says how many were read and suggests a smaller `--limit` or a narrower filter. The answer prints `next_cursor`, and `--cursor` continues from it. |
24
24
  | `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. |
@@ -73,9 +73,11 @@ product decision is needed and give the person the single action that lets the b
73
73
  covers nothing and says so. A `succeeded` one reports what `coverage.truncated` recorded. Anything
74
74
  else states the bound it ran under — a `sample` its ceilings, a `full` the whole dataset, and a
75
75
  `full` at `awaiting_sample_approval` the whole dataset plus the preview it waits on. The mode
76
- alone never establishes a truncation, which is the rule reference 6 states: a sample sized above
77
- every measured source cut nothing and is the build. A source with a request window is the
78
- exception: a sample reads its latest lookback span only, and the sentence says so. It is absent
76
+ alone never establishes a truncation. A sample sized above every measured source cut nothing.
77
+ It is still a test, and it belongs on a scratch table, as
78
+ [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table)
79
+ says. A source with a request window is the exception to the size rule. A sample reads its latest
80
+ lookback span only, and the sentence says so. It is absent
79
81
  on `refresh`, `backfill`, `compact` and `replay`, and it is never evidence that a run is
80
82
  executing; `status` is.
81
83
 
@@ -20,10 +20,16 @@ after every revision.
20
20
  `suggestions`. After the first full, run `mr-data recipe explain RECIPE_ID --json` again: the
21
21
  table route also judges the predecessor. The command exits 0 when the table is ready to refresh
22
22
  and 2 when a finding blocks it. An advisory finding (`blocks_refresh: false`) never changes the
23
- exit code. The receipt's `fast_features` says which of `table.partition`, `batch` and
24
- `reference` the recipe uses or misses, and which sources every run reads whole. Each missed
25
- feature carries a one-line fix. `fast_features_missed` lists them for a script. See
26
- [Fast builds](#fast-builds-tablepartition-batch-and-reference).
23
+ exit code. The receipt's `fast_features` has eight entries. Four say what a run can keep:
24
+ `table.partition`, `batch`, `reference`, and `resync_only_sources` for the sources every run
25
+ reads whole. Four say how fast a run fetches: `pace`, `concurrency`, `parallel_pagination` and
26
+ `ranged_parquet`. Each missed feature carries a one-line fix. `fast_features_missed` lists
27
+ them for a script. On a new table, fix each one, or say in the build message why it stays.
28
+ On a table that is already live, list the missed entries and ask the user first. Each fix
29
+ changes the recipe digest, so the next build is a full resync. A paid source is called again
30
+ for every per-row request whose saved answer no longer applies. See
31
+ [Fast builds](#fast-builds-tablepartition-batch-and-reference) and
32
+ [Fetching fast](#fetching-fast-pace-width-parallel-pages-and-ranged-parquet).
27
33
  2. `mr-data recipe forecast RECIPE_ID --digest DIGEST --json` before any `--full` that pays for
28
34
  calls. It sends nothing. Per source, it reports the request ceiling the document allows, the
29
35
  count the last run of the same source definition actually sent, the pace, and the runtime at
@@ -32,8 +38,9 @@ after every revision.
32
38
  collection rather than start the run. The forecast also reads observed limits. Each source
33
39
  with a history run gets `observed`: the rate that run actually reached and the runtime of the
34
40
  expected requests at that rate. A line flags a run that went under half its declared pace,
35
- because some other limit held it. On a Studio without the forecast route, a platform source is
36
- paced at its binding's stated rate and provider limits. `--no-observed` skips both reads.
41
+ because some other limit held it. On a Studio without the forecast route, the forecast bounds
42
+ a platform source by its binding's stated rate and provider limits. The declared gap still
43
+ applies under them. `--no-observed` skips both reads.
37
44
  3. `mr-data run --recipe RECIPE_ID --digest DIGEST --full --json`, followed as
38
45
  [the run lifecycle](live-run.md) describes. `mr-data watch RUN_ID` prints a `rate` line for
39
46
  each collection source about once a minute: members done, members a minute, and the time left
@@ -267,12 +274,14 @@ from the forecast.** `mr-data recipe forecast` prints a suggested
267
274
  plus a quarter. A cap near the real count makes a corpus that suddenly doubles fail with
268
275
  `ROW_REQUESTS_EXCEED_BUDGET` before any call is sent, rather than pay for the doubling.
269
276
  `limits.max_requests` must still be at least `pages.max_fetches_per_run` times one more than
270
- `pages.retries`. With no history the forecast says `EXPECTED_UNKNOWN`. Build a small `--sample`
271
- first, read its plan counts with `mr-data status RUN_ID --diagnose --json`, and size from them.
277
+ `pages.retries`. With no history the forecast says `EXPECTED_UNKNOWN`. Run a small bounded test
278
+ on a scratch table first, as
279
+ [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table)
280
+ says. Read its plan counts with `mr-data status RUN_ID --diagnose --json`, and size from them.
272
281
 
273
282
  ### Fast builds: `table.partition`, `batch` and `reference`
274
283
 
275
- `mr-data recipe explain` lists each of these under `fast_features`. Each is a recipe member, so
284
+ `mr-data recipe explain` lists each of these three under `fast_features`. Each is a recipe member, so
276
285
  adding one changes the digest and the table's next full is a resync. Make every change in one
277
286
  revision so the table pays once. Adopting `table.partition` on an existing table costs one full
278
287
  resync. For a paid source, ask the user before that resync, as the brief and forecast rules
@@ -342,6 +351,28 @@ call:
342
351
  reads it whole. The same digest changes nothing. A new digest makes that refresh recompute the
343
352
  whole table, so every version joins exactly one edition of the dump.
344
353
 
354
+ ### Fetching fast: pace, width, parallel pages and ranged Parquet
355
+
356
+ These four `fast_features` entries are about the rate a run fetches at. Each is a recipe member
357
+ too, so change them in the first revision.
358
+
359
+ | Entry | It reports | The fix |
360
+ | --- | --- | --- |
361
+ | `pace` | a page collection or a keyless request window that states no gap | state the gap the publisher documents. No documented limit: state 1000 ms and say so |
362
+ | `concurrency` | any stated `pages.concurrency` | remove it, unless a run's refusal told you to lower it or the publisher documents a limit on concurrent connections. Record the reason in a dataset note |
363
+ | `parallel_pagination` | a serial `GET` page walk over JSON on `authenticated.https.api_key@2.0.0`, on a source with no window | move to `@2.1.0` with the counted `pagination.parallel`, when the first page states its page count |
364
+ | `ranged_parquet` | a `parquet.tabular@1.0.0` pin on a public file with no request window, in a recipe that states no `execution_policy` | pin `parquet.tabular@1.1.0`, when the publisher answers byte ranges and the limits in [Readers](readers.md) fit |
365
+
366
+ A stated gap is never reported, whatever its size. A slow gap that is the publisher's limit is
367
+ correct. The entries read the document alone. They cannot know a publisher's limit, so every fix
368
+ is yours to check against the source.
369
+ On a table that is already live, list the missed entries and ask the user first. Each fix changes
370
+ the recipe digest, so the next build is a full resync. A paid source is called again for every
371
+ per-row request whose saved answer no longer applies.
372
+ [Pace and width](sources.md#pace-and-width),
373
+ [parallel pages](sources.md#paging-a-keyed-api-in-parallel) and [Readers](readers.md) have the
374
+ rules.
375
+
345
376
  ### Probing a request window without an executing probe
346
377
 
347
378
  `mr-data recipe explain` suggests `declare_request_window` for a source that reads whole. Its
@@ -366,7 +397,10 @@ bounded runs over two disjoint windows:
366
397
  5. Retire the scratch tables: `mr-data demote TABLE_A TABLE_B`, then
367
398
  `mr-data table archive TABLE_A TABLE_B --confirm DELETE`.
368
399
 
369
- Both runs spend real requests, so keep the clamp small. Nothing can be probed yet about response
400
+ These are scratch tables, as
401
+ [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table)
402
+ requires: never run this test on the real table. Both runs spend real requests, so keep the clamp
403
+ small. The row clamp cuts rows after the fetch. It does not shorten a collection walk. Nothing can be probed yet about response
370
404
  headers, `ETag` or `Last-Modified`, sort order, cursors, or how the publisher answers under 429 or
371
405
  5xx. No shipped command can vary a request or report those facts. Do not claim any of them from
372
406
  documentation alone.
@@ -595,6 +629,10 @@ For Binance BTCUSDT one-minute klines, start the backfill at `2017-08-17T00:00:0
595
629
  }
596
630
  ```
597
631
 
632
+ The 1000 above is one request a second. State the gap the publisher documents. With no documented
633
+ limit, state 1000 and say so. `mr-data recipe explain` reports a keyless window that states no
634
+ gap under `fast_features` as `pace`.
635
+
598
636
  Keep `granularity: day`, `timezone: UTC` and `merge.materialization: partition_replace`.
599
637
  For the acquired `open_time_ms` column declare
600
638
  `merge.partition: {"column":"open_time_ms","key":"epoch_millis_value"}` and
@@ -9,7 +9,7 @@ resolves no third-party package at all, and the engine, the Readers and the work
9
9
  Mostly Right Studio. `--hosted` is accepted and does nothing, because the lane it names is the only
10
10
  one; a script that already carries it keeps working.
11
11
 
12
- `mr-data --help` lists every command this product has — thirty-four of them, and the page is the
13
- whole list. A name that is not on it is not a command: typing one answers `THIN_UNKNOWN_COMMAND`,
12
+ `mr-data --help` lists every command this product has. There are thirty-eight, and the page is
13
+ the whole list. A name that is not on it is not a command: typing one answers `THIN_UNKNOWN_COMMAND`,
14
14
  exactly as a misspelling does. Do not retry it under a different install, and do not report its
15
15
  absence as a defect.
@@ -15,8 +15,9 @@ mr-data search inspect --recipe recipe.json --json
15
15
 
16
16
  Local CLI help describes installed commands; it does not prove the deployed server serves them.
17
17
  For the recipe-target probe above, read JSON refusals from stderr: `schema_version` is
18
- `mr-data-error.v1`, `code` is the typed discriminator, and `error` is the detail. There is no
19
- public `http_status` field in that envelope. Treat `code: THIN_NOT_FOUND` as absent search
18
+ `mr-data-error.v1`, `code` is the typed discriminator, and `error` is the detail. Cloud refusals
19
+ also expose `http_status`, `request_id`, `detail` and `retry_after_seconds` when supplied;
20
+ Studio inspection refusals retain their typed code. Treat `code: THIN_NOT_FOUND` as absent search
20
21
  registration support only when `error` begins exactly with
21
22
  `Studio has no such resource for /v4/search/recipe-inspection`; the CLI maps this route's HTTP 404
22
23
  that way. An explicit `code: THIN_SEARCH_CAPABILITY_UNAVAILABLE` also states a capability limit.
@@ -141,3 +142,28 @@ an exact filter.
141
142
 
142
143
  This workflow opts in newly authored recipes only. It does not migrate old datasets, reauthor users'
143
144
  existing recipes, launch a full data rebuild or imply successful indexing of every table.
145
+
146
+ ## Diagnose preparation and query a published table
147
+
148
+ `preparation_state: unavailable` can mean a never-built table, a failed or cancelled job,
149
+ or an incompatible specification. Keep `job_id` from the search build receipt and run
150
+ `mr-data search status --job JOB_ID --json` to inspect the job's status and failure.
151
+ Studio is adding `preparation_detail`; print it when present rather than inferring a reason
152
+ from `unavailable`. Table status passes this additive field through unchanged.
153
+
154
+ For a published table, first run `mr-data public schema TABLE_ID --json`.
155
+ The table schema's `capabilities.search` contains `table_version_id`, `spec_digest`,
156
+ `keyword_columns`, `semantic_columns`, and optional stored `lexical`/`semantic` preparation values.
157
+ Use `keyword_columns` for `search.columns`; top-level `columns` is the non-empty output projection.
158
+ The following lexical example uses a syntactically valid illustrative digest:
159
+
160
+ ```sh
161
+ mr-data public query TABLE_ID --body '{"columns":["title"],"search":{"text":"example","columns":["title"],"spec_digest":"sha256:0000000000000000000000000000000000000000000000000000000000000000","mode":"lexical"},"limit":10}' --json
162
+ ```
163
+
164
+ Replace the illustrative columns and digest with the actual schema values. Hybrid mode additionally
165
+ needs `generation_digest` (`sha256:` plus 64 lowercase hex digits) from readiness/explain;
166
+ it is not in `capabilities.search`. Omit it for this lexical example. Public query prints
167
+ Cloud stage durations in milliseconds under `result.timings` when Server-Timing is present,
168
+ and reports HTTP status, request id and retry timing. Keep refusal `detail` with the request id.
169
+ Missing timing metadata does not establish that a stage did no work.
@@ -1,7 +1,9 @@
1
1
  ## Promote
2
2
 
3
- A table's first succeeded run goes live on its own, whatever mode that run was — a run whose
4
- coverage is untruncated everywhere counts. There is no command that makes it readable and no click
3
+ A table's first succeeded run goes live on its own, whatever mode that run was and whether or
4
+ not a clamp truncated it. A sample counts. So test on a scratch table and never on the real one,
5
+ as [the probe rule](3-probe-read-a-source-before-committing-to-it.md#the-probe-rule-test-on-a-scratch-table-never-on-the-real-table)
6
+ says. There is no command that makes a table readable and no click
5
7
  to wait for, and no catch-up run is dispatched for it. Everything before it is workspace scratch
6
8
  too: probing, building, downloading and interrogating all run with no human ceremony at all.
7
9