mostlyright-data 0.31.11__tar.gz → 0.31.12__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 (139) hide show
  1. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/PKG-INFO +1 -1
  2. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/preparing-table-search.md +47 -3
  3. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/table_search_contract.json +44 -1
  4. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4.py +2 -2
  5. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_runs.py +10 -1
  6. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_search_contract.py +54 -3
  7. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/.gitignore +0 -0
  8. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/README.md +0 -0
  9. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/pyproject.toml +0 -0
  10. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/scripts/hatch_build.py +0 -0
  11. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/SKILL.md +0 -0
  12. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/agents/openai.yaml +0 -0
  13. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
  14. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
  15. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
  16. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
  17. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
  18. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +0 -0
  19. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
  20. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
  21. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
  22. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/agent-protocol.md +0 -0
  23. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
  24. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
  25. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/boundaries.md +0 -0
  26. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
  27. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/commands.md +0 -0
  28. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
  29. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/enriching-with-small-models.md +0 -0
  30. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/installation-parity.md +0 -0
  31. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/live-run.md +0 -0
  32. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/making-a-table-incremental.md +0 -0
  33. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/narrating-the-run.md +0 -0
  34. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
  35. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/one-install.md +0 -0
  36. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/patterns.md +0 -0
  37. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/prediction-labels.md +0 -0
  38. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/promote.md +0 -0
  39. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/readers.md +0 -0
  40. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/receipts.md +0 -0
  41. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/recipe-test.md +0 -0
  42. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
  43. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
  44. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/reference-pages.md +0 -0
  45. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/required-protocol.md +0 -0
  46. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/source-credentials.md +0 -0
  47. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/sources.md +0 -0
  48. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
  49. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/transforms.md +0 -0
  50. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/user-communication-contract.md +0 -0
  51. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
  52. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
  53. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/__init__.py +0 -0
  54. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/agent_protocol.py +0 -0
  55. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/canonical.py +0 -0
  56. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/formats.py +0 -0
  57. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
  58. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/key_seam.py +0 -0
  59. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/page_coverage.py +0 -0
  60. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
  61. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/session_probes.py +0 -0
  62. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/skill_assets.py +0 -0
  63. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/table_manifest.py +0 -0
  64. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/__init__.py +0 -0
  65. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/acquire.py +0 -0
  66. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
  67. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/activity.py +0 -0
  68. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/approvals.py +0 -0
  69. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/backoff.py +0 -0
  70. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/categories.py +0 -0
  71. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/commands.py +0 -0
  72. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
  73. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/delivery.py +0 -0
  74. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/description_shape.py +0 -0
  75. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/download.py +0 -0
  76. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/enrichment.py +0 -0
  77. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/fast_features.py +0 -0
  78. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/matching_benchmark.py +0 -0
  79. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/narrative.py +0 -0
  80. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/parity.py +0 -0
  81. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/probe.py +0 -0
  82. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/progress_rate.py +0 -0
  83. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
  84. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/propose.py +0 -0
  85. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/recipe.py +0 -0
  86. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
  87. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/recipe_explain.py +0 -0
  88. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/recipe_forecast.py +0 -0
  89. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
  90. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/recipe_test.py +0 -0
  91. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/refresh_rehearsal.py +0 -0
  92. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/release_check.py +0 -0
  93. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/research.py +0 -0
  94. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/router.py +0 -0
  95. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/run_diagnose.py +0 -0
  96. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/run_prefix.py +0 -0
  97. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/runs.py +0 -0
  98. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/session.py +0 -0
  99. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/stream.py +0 -0
  100. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
  101. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/studio_capabilities.py +0 -0
  102. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/support.py +0 -0
  103. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/table_view.py +0 -0
  104. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/transport.py +0 -0
  105. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
  106. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
  107. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
  108. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_connected.py +0 -0
  109. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
  110. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
  111. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_datasets.py +0 -0
  112. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
  113. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_matching.py +0 -0
  114. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
  115. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_public.py +0 -0
  116. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
  117. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
  118. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_search.py +0 -0
  119. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
  120. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
  121. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
  122. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
  123. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/__init__.py +0 -0
  124. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/attendance.py +0 -0
  125. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/clarification.py +0 -0
  126. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
  127. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
  128. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
  129. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
  130. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
  131. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
  132. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
  133. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
  134. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/credentials.py +0 -0
  135. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/login.py +0 -0
  136. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
  137. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
  138. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/src/mostlyright/data_harness/ux/remediation.py +0 -0
  139. {mostlyright_data-0.31.11 → mostlyright_data-0.31.12}/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.31.11
3
+ Version: 0.31.12
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/
@@ -194,7 +194,46 @@ column names, the descriptions and the planner. Never bind a word that two colum
194
194
  such as "growth", "change" or "best", unless the brief names the metric. "Highest growth"
195
195
  pointed at a revenue column turns "highest growth last 30 days" into a revenue sort.
196
196
 
197
- Studio caps a spec at 128 filter aliases and 64 sort aliases, and `search validate` refuses more.
197
+ ### Declare a floor for every rate
198
+
199
+ A growth, change, ratio or per-unit column ranks nonsense first when its base is tiny. Revenue
200
+ that grew from $5,000 to $80 million is 1,600,000% growth, and a film with three votes has a
201
+ perfect rating. Sorting by such a column shows shells and placeholders, not the rows the reader
202
+ asked for. For each sortable rate column, declare the column it is a rate of, and the smallest
203
+ base that makes the rate meaningful, in `order_floors` (contract 4.54.0, up to 32 entries):
204
+
205
+ ```json
206
+ "order_floors": [
207
+ {"column": "revenue_growth_pct", "base_column": "revenue_prior_year_usd", "min": 10000000},
208
+ {"column": "revenue_cagr_5y_pct", "base_column": "revenue_5y_ago_usd", "min": 10000000},
209
+ {"column": "vote_average", "base_column": "vote_count", "min": 100}
210
+ ]
211
+ ```
212
+
213
+ - `column` is in `sortable_columns` or is a sort alias's column. `base_column` is another numeric
214
+ column in `filterable_columns`. List each base early in `filterable_columns`: a package keeps 64
215
+ filters, role and alias columns first and the rest in spec order, and a base past that cut
216
+ loses its floor. `search validate TABLE --spec` names such a floor in `warnings` under
217
+ `/order_floors/`; treat that warning as a floor that will not apply.
218
+ - `min` is a whole number in the base column's own unit, written without a decimal point or
219
+ exponent: `10000000` on a column in dollars, `10` on a column in millions.
220
+ - Floors are declared on a search revision with `mr-data search configure --spec`, not in the
221
+ recipe's `table.search`, which refuses the member. A revision configured from the recipe's own
222
+ spec carries none, and a new version of a private table keeps them only when you configure the
223
+ revision again. A plan made without the planner, such as a keystroke search, applies no floor.
224
+ - A Studio older than contract 4.54.0 refuses the member as unknown. Configure without it there.
225
+ - When a plan orders by `column`, in either direction, Studio adds `base_column >= min` as a
226
+ filter. The reader sees it in the search summary and can remove it. A query that already filters
227
+ `base_column` keeps its own filter.
228
+ - Prefer the true base (the earlier period's revenue, the vote count) over a size proxy such as
229
+ market cap. When the table has no base column, add one in the transform: it is cheaper than a
230
+ wrong ranking.
231
+ - Choose `min` from the built table with `mr-data query`: the smallest base at which the rate stops
232
+ being dominated by placeholders. State the floor at the start of the base column's
233
+ description, since the planner reads only the first 160 characters of a description.
234
+
235
+ A recipe's spec takes 128 filter aliases and 64 sort aliases, and a search revision 512 and 256.
236
+ `search validate` refuses more.
198
237
  Rank the phrases and keep the top of the list. Put first the words that name the kind of row, the
199
238
  facets users type most, the popularity sorts and the product's own vocabulary. A phrase you leave
200
239
  out maps to nothing. The planner may still recover it from the column names and values, and
@@ -225,8 +264,13 @@ roles. It never sees rows.
225
264
  `planner_detail.reason`. `external_context_not_allowed` means the recipe withholds consent.
226
265
  `private_source` means a source is not public.
227
266
 
228
- Write each column description as if the planner reads it. Say what the column holds, its unit,
229
- and the words people use for it.
267
+ Write each column description as if the planner reads it, because it does, and the page shows it
268
+ to the reader beside results ordered by that column. Say what the column holds, the period it
269
+ covers ("trailing twelve months", "latest fiscal year against the year before", "over the last
270
+ five fiscal years"), its unit, and the words people use for it, abbreviations included
271
+ ("compound annual growth rate, CAGR"). The planner picks the column a query asks about, named
272
+ bare ("revenue") or in a question ("what is the P/E ratio?"), from these descriptions. It reads
273
+ the first 160 characters of each, so lead with what the column measures and its period.
230
274
 
231
275
  ### Name and describe columns for search
232
276
 
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "schema_version": "mostlyright-thin-table-search-contract.v1",
3
- "source_commit": "0dcecc1b8dfdd3f8960497fdfcffeeb72e240abd",
3
+ "source_commit": "5109da1f92eaaf4fc41ed35a03e5d010268d7c7b",
4
4
  "recipe_search_definition": "recipe_search",
5
5
  "$defs": {
6
+ "common.safe_integer": {
7
+ "description": "An integer exactly representable by both Python JSON integers and TypeScript numbers.",
8
+ "type": "integer",
9
+ "minimum": -9007199254740991,
10
+ "maximum": 9007199254740991
11
+ },
6
12
  "evaluation_case": {
7
13
  "type": "object",
8
14
  "additionalProperties": false,
@@ -119,6 +125,14 @@
119
125
  ]
120
126
  },
121
127
  "description": "Keel's recipe reader does not admit long_text_columns yet (4.42.0): a search revision declares it."
128
+ },
129
+ {
130
+ "not": {
131
+ "required": [
132
+ "order_floors"
133
+ ]
134
+ },
135
+ "description": "The recipe readers do not admit order_floors (4.54.0): a search revision declares it."
122
136
  }
123
137
  ]
124
138
  },
@@ -252,6 +266,35 @@
252
266
  "direction"
253
267
  ]
254
268
  },
269
+ "order_floors": {
270
+ "description": "Order floors (4.54.0): for a sortable column whose order means little on rows with a tiny or missing base (a growth rate on almost no revenue, a rating from a handful of votes), the base column and the least value of it at which a row answers that order. When a plan orders by `column`, either direction, and states no predicate on `base_column`, the planner adds `base_column >= min` (origin `parser`), which the search node runs and reports as any predicate. `column` is declared sortable (in `sortable_columns` or a sort alias's, even where a v3 package sorts numbers by default), `base_column` is another declared numeric column in `filterable_columns`, and each `column` has at most one floor. `min` is an integer (a request body carries no fractional or exponent number), in the base column's own unit. Admitted on a search revision; a recipe's `table.search` may not name it yet.",
271
+ "type": "array",
272
+ "maxItems": 32,
273
+ "items": {
274
+ "type": "object",
275
+ "additionalProperties": false,
276
+ "properties": {
277
+ "column": {
278
+ "type": "string",
279
+ "minLength": 1,
280
+ "maxLength": 128
281
+ },
282
+ "base_column": {
283
+ "type": "string",
284
+ "minLength": 1,
285
+ "maxLength": 128
286
+ },
287
+ "min": {
288
+ "$ref": "#/$defs/common.safe_integer"
289
+ }
290
+ },
291
+ "required": [
292
+ "column",
293
+ "base_column",
294
+ "min"
295
+ ]
296
+ }
297
+ },
255
298
  "planner": {
256
299
  "type": "object",
257
300
  "additionalProperties": false,
@@ -149,8 +149,8 @@ from mostlyright.data_harness.thin.v4_matching import CONTRACT_PATHS as MATCHING
149
149
  #: ``JOB_INVALID`` outright, which in production was every refresh a collection epoch was offered
150
150
  #: for rather than only the collection runs. This package must not reach a Studio older than the
151
151
  #: commit it pins; ``docs/V4-WORKER-PROTOCOL.md`` states it beside the layout's own ordering rule.
152
- PINNED_V4_OPENAPI_SOURCE_SHA256 = "b8f2ac008a714a12b88516d4c723ef8a7af18100c2678831fbdafd50fc6a5f20"
153
- PINNED_V4_CONTRACT_VERSION = "4.49.0"
152
+ PINNED_V4_OPENAPI_SOURCE_SHA256 = "e7bd4e994b32d62ae00b4d7fc2f8936e4964cd4160b7aa30ae010528ff57b1a8"
153
+ PINNED_V4_CONTRACT_VERSION = "4.54.0"
154
154
 
155
155
  #: The ``schema_version`` every command body that predates 4.14.0 stamps: ``dataset activity``,
156
156
  #: ``table rename``, ``table archive`` and ``dataset archive``. It is the number the 0.25 line
@@ -1494,7 +1494,16 @@ def _retry_refusal_step(run: Mapping[str, Any]) -> str:
1494
1494
  #: A source that kept refusing. Studio admits a person's named retry of these once its wait has
1495
1495
  #: passed and answers ``RUN_RETRY_TOO_SOON`` with a Retry-After before then (planning #1234), so
1496
1496
  #: this command sends them and lets Studio say when.
1497
- PERSON_RETRY_CODES = frozenset({"SOURCE_RATE_LIMITED", "SOURCE_HTTP_5XX", "SOURCE_PROXY_BUSY"})
1497
+ PERSON_RETRY_CODES = frozenset(
1498
+ {
1499
+ "SOURCE_RATE_LIMITED",
1500
+ "SOURCE_HTTP_5XX",
1501
+ "SOURCE_PROXY_BUSY",
1502
+ "TRANSPORT_FAILURE",
1503
+ "TOTAL_TIMEOUT",
1504
+ "DNS_TEMPORARY_FAILURE",
1505
+ }
1506
+ )
1498
1507
 
1499
1508
  #: Failure codes a named retry may take without a recipe change. A hand copy of Studio's
1500
1509
  #: ``agent_recovery`` retry and platform sets. The server refuses ``retry_of_run_id`` for anything
@@ -54,10 +54,57 @@ def _recipe_search_definition(recipe: dict[str, Any]) -> dict[str, Any]:
54
54
  return localize(recipe["$defs"]["table_plan"]["properties"]["search"])
55
55
 
56
56
 
57
- def build_bundle(table_search: dict[str, Any], recipe: dict[str, Any], pin: dict[str, Any]) -> dict:
58
- """The closure of the definitions the client checks, from the vendored documents."""
57
+ #: A reference into the common schema is carried into the bundle as a local definition named
58
+ #: with this prefix, so the bundle stays one self-contained document.
59
+ COMMON_REF = "common.schema.json#/$defs/"
60
+ COMMON_PREFIX = "common."
59
61
 
60
- definitions = table_search["$defs"]
62
+
63
+ def _localize_common(value: Any) -> Any:
64
+ """``value`` with every common-schema reference pointing at its bundled local copy."""
65
+
66
+ if isinstance(value, list):
67
+ return [_localize_common(item) for item in value]
68
+ if not isinstance(value, dict):
69
+ return value
70
+ result = {}
71
+ for key, item in value.items():
72
+ if key == "$ref" and isinstance(item, str) and item.startswith(COMMON_REF):
73
+ result[key] = "#/$defs/" + COMMON_PREFIX + item[len(COMMON_REF) :]
74
+ else:
75
+ result[key] = _localize_common(item)
76
+ return result
77
+
78
+
79
+ def build_bundle(
80
+ table_search: dict[str, Any],
81
+ recipe: dict[str, Any],
82
+ pin: dict[str, Any],
83
+ common: dict[str, Any] | None = None,
84
+ ) -> dict:
85
+ """The closure of the definitions the client checks, from the vendored documents. A
86
+ definition the search schema takes from the common schema is copied in under
87
+ :data:`COMMON_PREFIX`."""
88
+
89
+ def within_common(value: Any) -> Any:
90
+ """A common definition with its own local references pointing at their copies."""
91
+
92
+ if isinstance(value, list):
93
+ return [within_common(item) for item in value]
94
+ if not isinstance(value, dict):
95
+ return value
96
+ return {
97
+ key: (
98
+ "#/$defs/" + COMMON_PREFIX + _ref_name(item)
99
+ if key == "$ref" and isinstance(item, str) and item.startswith("#/$defs/")
100
+ else within_common(item)
101
+ )
102
+ for key, item in value.items()
103
+ }
104
+
105
+ definitions = dict(table_search["$defs"])
106
+ for name, definition in ((common or {}).get("$defs") or {}).items():
107
+ definitions[COMMON_PREFIX + name] = within_common(definition)
61
108
  recipe_member = "recipe_search"
62
109
  wanted = [SPEC, recipe_member, CASES]
63
110
  selected: dict[str, Any] = {recipe_member: _recipe_search_definition(recipe)}
@@ -66,6 +113,8 @@ def build_bundle(table_search: dict[str, Any], recipe: dict[str, Any], pin: dict
66
113
  if isinstance(value, dict):
67
114
  ref = value.get("$ref")
68
115
  if isinstance(ref, str):
116
+ if ref.startswith(COMMON_REF):
117
+ ref = "#/$defs/" + COMMON_PREFIX + ref[len(COMMON_REF) :]
69
118
  name = _ref_name(ref)
70
119
  if name not in selected:
71
120
  selected[name] = definitions[name]
@@ -81,6 +130,7 @@ def build_bundle(table_search: dict[str, Any], recipe: dict[str, Any], pin: dict
81
130
  if name not in selected:
82
131
  selected[name] = definitions[name]
83
132
  collect(definitions[name])
133
+ selected = {name: _localize_common(definition) for name, definition in selected.items()}
84
134
  return {
85
135
  "schema_version": BUNDLE_SCHEMA_VERSION,
86
136
  "source_commit": pin["source_commit"],
@@ -95,6 +145,7 @@ def bundle_from_vendor(vendor: Path = VENDOR) -> dict[str, Any]:
95
145
  json.loads((schemas / "table-search.schema.json").read_text(encoding="utf-8")),
96
146
  json.loads((schemas / "recipe.schema.json").read_text(encoding="utf-8")),
97
147
  json.loads((vendor / "client-v4-pin.json").read_text(encoding="utf-8")),
148
+ json.loads((schemas / "common.schema.json").read_text(encoding="utf-8")),
98
149
  )
99
150
 
100
151