mostlyright-data 0.31.13__tar.gz → 0.31.14__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.
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/PKG-INFO +1 -1
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/commands.md +1 -1
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/patterns.md +3 -2
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/readers.md +12 -2
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/sources.md +4 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe.py +2 -2
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/session.py +25 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/support.py +32 -8
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/table_search_contract.json +8 -2
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4.py +2 -2
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_datasets.py +19 -6
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_query.py +93 -6
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_reader.py +26 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_search.py +3 -1
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/remediation.py +12 -1
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/.gitignore +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/README.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/pyproject.toml +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/SKILL.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/agent-protocol.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/boundaries.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/enriching-with-small-models.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/installation-parity.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/live-run.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/making-a-table-incremental.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/narrating-the-run.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/one-install.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/prediction-labels.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/preparing-table-search.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/promote.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/receipts.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/recipe-test.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/reference-pages.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/required-protocol.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/source-credentials.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/transforms.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/user-communication-contract.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/agent_protocol.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/table_manifest.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/backoff.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/delivery.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/description_shape.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/enrichment.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/fast_features.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/matching_benchmark.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/progress_rate.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe_explain.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe_forecast.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe_test.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/refresh_rehearsal.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/release_check.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/router.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/run_diagnose.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/run_prefix.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/studio_capabilities.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/table_view.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_connected.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_matching.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_public.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_search_contract.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/attendance.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/render.py +0 -0
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/commands.md
RENAMED
|
@@ -17,7 +17,7 @@ run in full.
|
|
|
17
17
|
| `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. |
|
|
18
18
|
| `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. |
|
|
19
19
|
| `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. |
|
|
20
|
-
| `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 `
|
|
20
|
+
| `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`, `decode_options_json` and `options_digest`. The second is the text a recipe carries as `reader.decode_options`, and the third is the `reader.options_digest` registration accepts for exactly those 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. |
|
|
21
21
|
| `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. |
|
|
22
22
|
| `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. |
|
|
23
23
|
| `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. |
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/patterns.md
RENAMED
|
@@ -225,8 +225,9 @@ for the days that can still change, through the two request `parameters` it rend
|
|
|
225
225
|
unit reads its own page count, so size `limits.max_requests` from a measured unit.
|
|
226
226
|
[Paging a keyed API in parallel](sources.md#paging-a-keyed-api-in-parallel) has the rules.
|
|
227
227
|
|
|
228
|
-
`options_digest` is the SHA-256 of the canonical JSON of `decode_options
|
|
229
|
-
|
|
228
|
+
`options_digest` is the SHA-256 of the canonical JSON of `decode_options`, and
|
|
229
|
+
`mr-data reader normalize` prints it for the options it returns. Registration names the digest it
|
|
230
|
+
expected when the two disagree.
|
|
230
231
|
|
|
231
232
|
```json
|
|
232
233
|
{
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/readers.md
RENAMED
|
@@ -57,9 +57,17 @@ mr-data reader normalize --family-id json.tabular --family-version 1.1.0 \
|
|
|
57
57
|
|
|
58
58
|
`options.json` holds the keys the source needs, at most 64 KiB. Studio applies the family's own
|
|
59
59
|
defaults and option rules. The answer's `decode_options_json` is the text to write as
|
|
60
|
-
`reader.decode_options
|
|
60
|
+
`reader.decode_options`, and its `options_digest` is the `reader.options_digest` to pin beside it.
|
|
61
|
+
That digest binds the default-filled options the answer returns, not the subset in `options.json`,
|
|
62
|
+
so copy both together. A refusal arrives here, before any run is spent. Run it for every
|
|
61
63
|
Reader pin before you register.
|
|
62
64
|
|
|
65
|
+
`normalize` checks the options against the family, not against the place the pin goes. A
|
|
66
|
+
Generic API `generic_api.response.reader` admits fewer coordinates: `json.tabular` `1.0.0`,
|
|
67
|
+
`1.1.0` or `1.2.0`, `delimited_text` `1.0.0` or `1.1.0`, and `parquet.tabular` `1.0.0`.
|
|
68
|
+
`json.tabular` `1.3.0` and `1.4.0` normalize but are refused there. The highest-version advice
|
|
69
|
+
below applies inside that set.
|
|
70
|
+
|
|
63
71
|
A column that may be absent from a record must state `"required": false`. Publishers routinely omit
|
|
64
72
|
a key rather than sending null, and a required column that is missing refuses the record.
|
|
65
73
|
|
|
@@ -87,6 +95,7 @@ decoder. Zero matches and multiple matches both refuse.
|
|
|
87
95
|
| `archive.zip@1.0.0` | `member`; `member_format` (`csv` or `tsv`); optional `header: true`. |
|
|
88
96
|
| `archive.zip@1.1.0` | Same, plus `member_format: semicolon` for publishers such as DWD. |
|
|
89
97
|
| `archive.zip@1.2.0` | Same as `archive.zip@1.1.0`; also accepts a response served as `application/octet-stream`. |
|
|
98
|
+
| `archive.zip@4.0.0` | Exact safe `member`, `member_format: lines` and `encoding: utf-8 or cp1252`; no header. Streams the member's physical lines as line_number, raw_line, as `archive.tar@2.0.0` does for one member. |
|
|
90
99
|
| `archive.gzip@1.0.0` | `member_format`; optional `header: true`. `member` is invalid. |
|
|
91
100
|
| `archive.gzip@1.1.0` | Same; also accepts a response served as `application/octet-stream`. |
|
|
92
101
|
| `archive.tar@1.0.0` | Same as `archive.zip@1.0.0`. |
|
|
@@ -181,7 +190,8 @@ Use `public.https.api@1.0.0` only when `keyless_window` is literal `true`. Other
|
|
|
181
190
|
The new adapter uses the existing `generic_api.response` Reader contract. For Binance klines,
|
|
182
191
|
state `format: json`, `records_pointer: ""`, and `reader.family_id: json.tabular` with
|
|
183
192
|
`family_version: 1.0.0` (or the already admitted `1.1.0`). Pin `options_digest` to the SHA-256
|
|
184
|
-
of the canonical decode options,
|
|
193
|
+
of the canonical decode options, the `options_digest` `mr-data reader normalize` prints, rather
|
|
194
|
+
than inserting a placeholder digest. These decode
|
|
185
195
|
options project a root JSON array of arrays, with RFC 6901 numeric indexes into each row:
|
|
186
196
|
|
|
187
197
|
```json
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/sources.md
RENAMED
|
@@ -335,6 +335,10 @@ to separate them. No per-row collection may derive its rows from a `closed` or `
|
|
|
335
335
|
`pagination` is a `page` or `offset` walk with a required `parallel` member, in one of two forms.
|
|
336
336
|
Prefer the counted form.
|
|
337
337
|
|
|
338
|
+
`@2.1.0` refuses `{"kind": "none", "max_pages": 1}`, and `cursor` and `link` walks too. For one
|
|
339
|
+
request, use `@2.0.0`. Use `authenticated.https.connector@3.0.0` instead when the request
|
|
340
|
+
declares a `per_call` cost, which `@2.0.0` does not admit.
|
|
341
|
+
|
|
338
342
|
| Form | Members | Use it when |
|
|
339
343
|
| --- | --- | --- |
|
|
340
344
|
| counted | `total_pages_pointer`, `max_page_bytes`, `min_interval_milliseconds`, optional `concurrency` | the first page states how many pages exist. A `page` walk over a `json` response only |
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/recipe.py
RENAMED
|
@@ -170,12 +170,12 @@ FILE_DECODER_COORDINATES: frozenset[tuple[str, str]] = frozenset(
|
|
|
170
170
|
for family, versions in {
|
|
171
171
|
"archive.gzip": ("1.0.0", "1.1.0", "2.0.0", "2.1.0"),
|
|
172
172
|
"archive.tar": ("1.0.0", "1.1.0", "3.0.0", "4.0.0"),
|
|
173
|
-
"archive.zip": ("1.0.0", "1.1.0", "1.2.0", "1.3.0", "1.4.0", "1.5.0", "3.0.0"),
|
|
173
|
+
"archive.zip": ("1.0.0", "1.1.0", "1.2.0", "1.3.0", "1.4.0", "1.5.0", "3.0.0", "4.0.0"),
|
|
174
174
|
"delimited_text": ("1.0.0", "1.1.0", "2.0.0"),
|
|
175
175
|
"geoparquet.vector": ("1.0.0",),
|
|
176
176
|
"json.tabular": ("1.1.0", "1.2.0"),
|
|
177
177
|
"meteorology.bufr": ("1.0.0",),
|
|
178
|
-
"meteorology.grib": ("1.0.0",),
|
|
178
|
+
"meteorology.grib": ("1.0.0", "2.0.0"),
|
|
179
179
|
"microdata.sas_xpt": ("1.0.0",),
|
|
180
180
|
"microdata.spss_sav": ("1.0.0",),
|
|
181
181
|
"microdata.stata_dta": ("1.0.0",),
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/session.py
RENAMED
|
@@ -587,6 +587,19 @@ def sessions_for(session: StudioSession | StudioSessionProvider) -> StudioSessio
|
|
|
587
587
|
#: a sentence with spaces in it.
|
|
588
588
|
CLOUD_REFUSAL_CODE = re.compile(r"[A-Z][A-Z0-9_]{0,63}")
|
|
589
589
|
|
|
590
|
+
#: Cloud's refusal of a web chat sandbox key on a route that would change something
|
|
591
|
+
#: (Cloud ``lib/sandbox-key-refusal.ts``). The key is valid and reads; every change it
|
|
592
|
+
#: would make goes through the chat's own approval-gated tools or a person on the dashboard.
|
|
593
|
+
#: Folded onto ``THIN_AUTHORIZATION_DENIED`` it told an agent in the web chat to run
|
|
594
|
+
#: ``mr-data login``, which cannot help, so it keeps its own code -- and Cloud's sentence,
|
|
595
|
+
#: which names the chat tool that does the job.
|
|
596
|
+
SANDBOX_KEY_READ_ONLY_CODE = "SANDBOX_KEY_READ_ONLY"
|
|
597
|
+
THIN_SANDBOX_KEY_READ_ONLY = f"THIN_CLOUD_{SANDBOX_KEY_READ_ONLY_CODE}"
|
|
598
|
+
SANDBOX_KEY_READ_ONLY_SENTENCE = (
|
|
599
|
+
"This is a web chat key: it reads, and changes go through the chat's own tools or a "
|
|
600
|
+
"person on the dashboard; signing in again will not change that"
|
|
601
|
+
)
|
|
602
|
+
|
|
590
603
|
#: The family of Studio's refusals of a call that would skip Cloud's usage meter
|
|
591
604
|
#: (``DATA_EGRESS_THROUGH_CLOUD``). From a Cloud route it means Studio refused CLOUD's call, which
|
|
592
605
|
#: is Mostly Right's misconfiguration and never the caller's key.
|
|
@@ -844,6 +857,18 @@ def cloud_refusal(
|
|
|
844
857
|
"this computer's or the device key's; nothing here needs to change. Run the command "
|
|
845
858
|
"again later",
|
|
846
859
|
)
|
|
860
|
+
elif status == 403 and named == SANDBOX_KEY_READ_ONLY_CODE:
|
|
861
|
+
# NOT A MISSING OR EXPIRED KEY, so not the shared authorization code: the chat's
|
|
862
|
+
# key is valid and only this change is not its to make. Cloud's sentence names the
|
|
863
|
+
# tool that makes it, so it is quoted (bounded, printable, attributed) even though a
|
|
864
|
+
# code was named, which no other named refusal here does.
|
|
865
|
+
stated_alternative = _cloud_sentence(parsed.get("error"))
|
|
866
|
+
refusal = ThinLaneError(
|
|
867
|
+
THIN_SANDBOX_KEY_READ_ONLY,
|
|
868
|
+
f"{SANDBOX_KEY_READ_ONLY_SENTENCE}. Cloud said: {stated_alternative}"
|
|
869
|
+
if stated_alternative
|
|
870
|
+
else SANDBOX_KEY_READ_ONLY_SENTENCE,
|
|
871
|
+
)
|
|
847
872
|
elif status in {401, 403}:
|
|
848
873
|
if named:
|
|
849
874
|
suffix = f" (Cloud said {named}, HTTP {status})"
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/support.py
RENAMED
|
@@ -31,7 +31,11 @@ from typing import Any
|
|
|
31
31
|
|
|
32
32
|
from mostlyright.data_harness.thin import THIN_SCHEMA_PREFIX
|
|
33
33
|
from mostlyright.data_harness.thin.backoff import jittered
|
|
34
|
-
from mostlyright.data_harness.thin.session import
|
|
34
|
+
from mostlyright.data_harness.thin.session import (
|
|
35
|
+
THIN_SANDBOX_KEY_READ_ONLY,
|
|
36
|
+
CloudClient,
|
|
37
|
+
require_device_key,
|
|
38
|
+
)
|
|
35
39
|
from mostlyright.data_harness.thin.transport import ThinLaneError, UrlLibTransport
|
|
36
40
|
from mostlyright.data_harness.ux.credentials import resolve_cloud_credentials
|
|
37
41
|
|
|
@@ -61,6 +65,14 @@ SETTLED = frozenset({"answered", "budget_exhausted", "failed"})
|
|
|
61
65
|
SETTLED_RECEIPTS = frozenset(f"support_{state}" for state in SETTLED)
|
|
62
66
|
STATUS_VERB = "status"
|
|
63
67
|
|
|
68
|
+
#: What a web chat key is told when Cloud refuses it this route. Said here as well as by
|
|
69
|
+
#: Cloud, so an agent in the chat learns the tool even if Cloud's sentence did not survive
|
|
70
|
+
#: the bound on quoted text.
|
|
71
|
+
CHAT_SUPPORT_TOOLS = (
|
|
72
|
+
"In the web chat, ask with the chat's ask_support tool, then read the answer with "
|
|
73
|
+
"get_support_answer"
|
|
74
|
+
)
|
|
75
|
+
|
|
64
76
|
|
|
65
77
|
def declare_arguments(parser: argparse.ArgumentParser) -> None:
|
|
66
78
|
"""Add `ask`'s arguments to the shared hosted parser."""
|
|
@@ -106,17 +118,29 @@ def declare_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
106
118
|
)
|
|
107
119
|
|
|
108
120
|
|
|
121
|
+
def _name_chat_tools(failure: ThinLaneError) -> ThinLaneError:
|
|
122
|
+
"""A web chat key refused here is pointed at the chat's own support tools."""
|
|
123
|
+
|
|
124
|
+
if failure.code == THIN_SANDBOX_KEY_READ_ONLY and "ask_support" not in failure.detail:
|
|
125
|
+
failure.detail = f"{failure.detail}. {CHAT_SUPPORT_TOOLS}"
|
|
126
|
+
failure.args = (f"{failure.code}: {failure.detail}",)
|
|
127
|
+
return failure
|
|
128
|
+
|
|
129
|
+
|
|
109
130
|
class SupportClient(CloudClient):
|
|
110
131
|
"""The two support routes over the shared device-key Cloud client."""
|
|
111
132
|
|
|
112
133
|
def submit(self, body: Mapping[str, Any]) -> dict[str, Any]:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
try:
|
|
135
|
+
return self.call(
|
|
136
|
+
"POST",
|
|
137
|
+
CLOUD_SUPPORT_QUESTIONS_PATH,
|
|
138
|
+
act="ask the support agent a question",
|
|
139
|
+
body=body,
|
|
140
|
+
expected=(202,),
|
|
141
|
+
)
|
|
142
|
+
except ThinLaneError as failure:
|
|
143
|
+
raise _name_chat_tools(failure) from None
|
|
120
144
|
|
|
121
145
|
def read(self, question_id: str, *, run: bool) -> dict[str, Any]:
|
|
122
146
|
suffix = "" if run else "?wait=0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "mostlyright-thin-table-search-contract.v1",
|
|
3
|
-
"source_commit": "
|
|
3
|
+
"source_commit": "b296c21484f40cce334e1b7c0f6c7c03b44a41e9",
|
|
4
4
|
"recipe_search_definition": "recipe_search",
|
|
5
5
|
"$defs": {
|
|
6
6
|
"common.safe_integer": {
|
|
@@ -234,6 +234,12 @@
|
|
|
234
234
|
"asc",
|
|
235
235
|
"desc"
|
|
236
236
|
]
|
|
237
|
+
},
|
|
238
|
+
"quality": {
|
|
239
|
+
"description": "Optional owner quality note (4.55.0). A string of 1 to 200 characters. Shown on the order question only when this phrase occurs in the query. It does not change the sort. A boolean is invalid.",
|
|
240
|
+
"type": "string",
|
|
241
|
+
"minLength": 1,
|
|
242
|
+
"maxLength": 200
|
|
237
243
|
}
|
|
238
244
|
},
|
|
239
245
|
"required": [
|
|
@@ -242,7 +248,7 @@
|
|
|
242
248
|
"direction"
|
|
243
249
|
]
|
|
244
250
|
},
|
|
245
|
-
"description": "Order phrases (4.45.0: at most 256), one package order key each, under the aliases' shared byte budget."
|
|
251
|
+
"description": "Order phrases (4.45.0: at most 256), one package order key each, under the aliases' shared byte budget. Optional quality (4.55.0) is the owner's note for that phrase."
|
|
246
252
|
},
|
|
247
253
|
"default_order": {
|
|
248
254
|
"description": "The order of a search with nothing to rank by (4.44.0): a browse or an entity plan with no order of its own walks the rows in this order instead of the package's row order (popularity). Mojirama's `known` (vote count descending) for a film table; the column must be sortable (declared sortable, a sort alias's, or the popularity or votes role).",
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4.py
RENAMED
|
@@ -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 = "
|
|
153
|
-
PINNED_V4_CONTRACT_VERSION = "4.
|
|
152
|
+
PINNED_V4_OPENAPI_SOURCE_SHA256 = "b378d4f70d580aed094b00edeec5acc39cae5c79f19dc4e869fe40304fccf654"
|
|
153
|
+
PINNED_V4_CONTRACT_VERSION = "4.57.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
|
|
@@ -136,6 +136,7 @@ from mostlyright.data_harness.thin.session import (
|
|
|
136
136
|
CLOUD_REFUSAL_CODE,
|
|
137
137
|
MAX_JSON_RESPONSE_BYTES,
|
|
138
138
|
MAX_STATED_MESSAGE_CHARS,
|
|
139
|
+
THIN_SANDBOX_KEY_READ_ONLY,
|
|
139
140
|
CloudClient,
|
|
140
141
|
StudioSession,
|
|
141
142
|
StudioSessionProvider,
|
|
@@ -1615,18 +1616,30 @@ def _sync_written_identity(dataset_id: str, cloud_url: str) -> dict[str, Any]:
|
|
|
1615
1616
|
)
|
|
1616
1617
|
return {"public_projection_synced": True}
|
|
1617
1618
|
except (ThinLaneError, LoginError, OSError) as error:
|
|
1618
|
-
|
|
1619
|
+
stated = isinstance(error, ThinLaneError) and getattr(error, "http_status", None) != 404
|
|
1620
|
+
result: dict[str, Any] = {
|
|
1619
1621
|
"public_projection_synced": False,
|
|
1620
|
-
"public_projection_error": (
|
|
1621
|
-
error.code
|
|
1622
|
-
if isinstance(error, ThinLaneError) and getattr(error, "http_status", None) != 404
|
|
1623
|
-
else "THIN_IDENTITY_SYNC_UNAVAILABLE"
|
|
1624
|
-
),
|
|
1622
|
+
"public_projection_error": (error.code if stated else "THIN_IDENTITY_SYNC_UNAVAILABLE"),
|
|
1625
1623
|
"public_projection_note": (
|
|
1626
1624
|
"The dataset was saved, but its public page could not be updated. "
|
|
1627
1625
|
f"Retry only the sync with: mr-data dataset sync {dataset_id}"
|
|
1628
1626
|
),
|
|
1629
1627
|
}
|
|
1628
|
+
# ⚠ THE CODE ALONE SENT PEOPLE TO THE WRONG FIX. Every refusal on this path left
|
|
1629
|
+
# as a bare code, so a key Cloud would never let update the page and an expired one
|
|
1630
|
+
# read the same. The refusal's own sentence -- this lane's words, with Cloud's
|
|
1631
|
+
# quoted inside them already bounded and printable by ``cloud_refusal`` -- rides
|
|
1632
|
+
# along. Nothing branches on it; the code beside it is what a script reads.
|
|
1633
|
+
if stated:
|
|
1634
|
+
result["public_projection_message"] = error.detail
|
|
1635
|
+
if stated and error.code == THIN_SANDBOX_KEY_READ_ONLY:
|
|
1636
|
+
# Retrying with this key is refused the same way: the chat does it.
|
|
1637
|
+
result["public_projection_note"] = (
|
|
1638
|
+
"The dataset was saved, but this web chat key cannot update its public "
|
|
1639
|
+
"page. Use the chat's update_dataset tool with sync_only: true, or a "
|
|
1640
|
+
"person syncs it from the dashboard."
|
|
1641
|
+
)
|
|
1642
|
+
return result
|
|
1630
1643
|
|
|
1631
1644
|
|
|
1632
1645
|
def _held(record: Mapping[str, Any], member: str) -> Any:
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_query.py
RENAMED
|
@@ -43,6 +43,7 @@ import os
|
|
|
43
43
|
import re
|
|
44
44
|
import time
|
|
45
45
|
from collections.abc import Callable, Mapping
|
|
46
|
+
from datetime import datetime
|
|
46
47
|
from http.client import HTTPException
|
|
47
48
|
from pathlib import Path
|
|
48
49
|
from typing import Any
|
|
@@ -124,6 +125,20 @@ MAX_QUERY_WAIT_SECONDS = 120.0
|
|
|
124
125
|
QUERY_POLL_INITIAL_SECONDS = 0.2
|
|
125
126
|
QUERY_POLL_MAX_SECONDS = 2.0
|
|
126
127
|
|
|
128
|
+
#: How long past the query's OWN deadline this command keeps reading before it stops.
|
|
129
|
+
#:
|
|
130
|
+
#: ⚠ THE SERVER'S DEADLINE BOUNDS THE WAIT TOO, NOT ONLY THIS CLIENT'S PATIENCE. Studio's deadline
|
|
131
|
+
#: (``deadline_at - submitted_at``, thirty seconds by default) is shorter than the two ceilings
|
|
132
|
+
#: above, so a query no worker took used to be read back for over a minute after Studio had stopped
|
|
133
|
+
#: caring about it, and then reported as merely "still running". The window is measured on this
|
|
134
|
+
#: computer's monotonic clock from the first read, using the server's own two stamps for its
|
|
135
|
+
#: length, so a skewed wall clock here can neither cut it short nor stretch it. The grace covers
|
|
136
|
+
#: a settle that lands on the deadline itself.
|
|
137
|
+
QUERY_DEADLINE_GRACE_SECONDS = 5.0
|
|
138
|
+
|
|
139
|
+
#: The refusal a query gets when Studio still reports it unsettled after its own deadline.
|
|
140
|
+
QUERY_DEADLINE_PASSED_CODE = "THIN_QUERY_DEADLINE_PASSED"
|
|
141
|
+
|
|
127
142
|
#: The submit body's bound, in canonical-JSON bytes, and the same number Studio enforces. Checked
|
|
128
143
|
#: here so an oversized submission is a sentence at the terminal rather than a 422 after a token
|
|
129
144
|
#: has been minted -- the reason every early refusal in this repository exists.
|
|
@@ -1071,7 +1086,7 @@ def _settled(
|
|
|
1071
1086
|
query_id = _record_identifier(record, run_id)
|
|
1072
1087
|
checkpoint_content_digest = _checkpoint_binding(record, checkpoint_id)
|
|
1073
1088
|
try:
|
|
1074
|
-
record, read = _poll(
|
|
1089
|
+
record, read, overdue = _poll(
|
|
1075
1090
|
selected,
|
|
1076
1091
|
run_id,
|
|
1077
1092
|
query_id,
|
|
@@ -1082,6 +1097,8 @@ def _settled(
|
|
|
1082
1097
|
)
|
|
1083
1098
|
except ThinLaneError as refusal:
|
|
1084
1099
|
raise _while_reading_back(refusal, query_id) from None
|
|
1100
|
+
if overdue and record.get("state") not in TERMINAL_QUERY_STATES:
|
|
1101
|
+
raise _deadline_passed(record, query_id, read)
|
|
1085
1102
|
head: dict[str, Any] = {
|
|
1086
1103
|
"schema_version": QUERY_SCHEMA,
|
|
1087
1104
|
"lane": "hosted",
|
|
@@ -1134,6 +1151,58 @@ def _settled(
|
|
|
1134
1151
|
return payload
|
|
1135
1152
|
|
|
1136
1153
|
|
|
1154
|
+
def _server_window_seconds(record: Mapping[str, Any]) -> float | None:
|
|
1155
|
+
"""The deadline Studio gave this query, in seconds after its submission, or ``None``.
|
|
1156
|
+
|
|
1157
|
+
Read from the record's own two stamps rather than compared with this computer's wall clock,
|
|
1158
|
+
which can be minutes off from Studio's.
|
|
1159
|
+
"""
|
|
1160
|
+
|
|
1161
|
+
submitted = record.get("submitted_at")
|
|
1162
|
+
deadline = record.get("deadline_at")
|
|
1163
|
+
if not isinstance(submitted, str) or not isinstance(deadline, str):
|
|
1164
|
+
return None
|
|
1165
|
+
try:
|
|
1166
|
+
ends = datetime.fromisoformat(deadline.replace("Z", "+00:00"))
|
|
1167
|
+
began = datetime.fromisoformat(submitted.replace("Z", "+00:00"))
|
|
1168
|
+
except ValueError:
|
|
1169
|
+
return None
|
|
1170
|
+
# A stamp without an offset is not an instant, and subtracting a naive one from an aware one
|
|
1171
|
+
# raises TypeError: either way the record states no window, and the old ceilings decide.
|
|
1172
|
+
if ends.tzinfo is None or began.tzinfo is None:
|
|
1173
|
+
return None
|
|
1174
|
+
try:
|
|
1175
|
+
window = (ends - began).total_seconds()
|
|
1176
|
+
except (TypeError, OverflowError):
|
|
1177
|
+
return None
|
|
1178
|
+
return window if window >= 0 else None
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
def _deadline_passed(record: Mapping[str, Any], query_id: str, read: int) -> ThinLaneError:
|
|
1182
|
+
"""A query Studio still calls unsettled after its own deadline, as the terminal refusal it is.
|
|
1183
|
+
|
|
1184
|
+
Not "still running": past ``deadline_at`` nothing will answer this query, and an agent told to
|
|
1185
|
+
wait would wait for nothing. What it can do is said: run the command again, which queues the
|
|
1186
|
+
same question afresh because a query past its deadline is re-run rather than replayed, and, if
|
|
1187
|
+
that also stalls, read the table without a worker.
|
|
1188
|
+
"""
|
|
1189
|
+
|
|
1190
|
+
state = record.get("state")
|
|
1191
|
+
refusal = ThinLaneError(
|
|
1192
|
+
QUERY_DEADLINE_PASSED_CODE,
|
|
1193
|
+
f"query {query_id} was still {state} {int(QUERY_DEADLINE_GRACE_SECONDS)} seconds after "
|
|
1194
|
+
f"its deadline {record.get('deadline_at')} ({read} reads), so no worker answered it in "
|
|
1195
|
+
"time and nothing was returned. Running this same command again queues the question "
|
|
1196
|
+
"afresh under the same identifier. If it stalls again, no session worker is taking this "
|
|
1197
|
+
"workspace's queries: mr-data download "
|
|
1198
|
+
f"{record.get('run_id') or 'RUN'} brings the table here to query locally",
|
|
1199
|
+
)
|
|
1200
|
+
refusal.query_id = query_id # type: ignore[attr-defined]
|
|
1201
|
+
refusal.deadline_at = record.get("deadline_at") # type: ignore[attr-defined]
|
|
1202
|
+
refusal.state = state # type: ignore[attr-defined]
|
|
1203
|
+
return refusal
|
|
1204
|
+
|
|
1205
|
+
|
|
1137
1206
|
#: The file the queries this computer has asked are remembered in, beside the release check's.
|
|
1138
1207
|
QUERY_MEMO_NAME = "queries.json"
|
|
1139
1208
|
|
|
@@ -1450,15 +1519,20 @@ def _poll(
|
|
|
1450
1519
|
checkpoint_content_digest: str | None,
|
|
1451
1520
|
sleep: Callable[[float], None],
|
|
1452
1521
|
clock: Callable[[], float],
|
|
1453
|
-
) -> tuple[dict[str, Any], int]:
|
|
1454
|
-
"""Read the record back until it settles,
|
|
1522
|
+
) -> tuple[dict[str, Any], int, bool]:
|
|
1523
|
+
"""Read the record back until it settles, until one of the two ceilings arrives, or until
|
|
1524
|
+
the query's own deadline (plus :data:`QUERY_DEADLINE_GRACE_SECONDS`) has passed.
|
|
1455
1525
|
|
|
1456
1526
|
The first read is taken immediately rather than after a sleep: what is in hand on entry is the
|
|
1457
1527
|
SUBMIT's own answer, written before any worker could have claimed it, so a query that settled
|
|
1458
1528
|
while the submit was returning would otherwise be reported a whole interval after the fact.
|
|
1529
|
+
|
|
1530
|
+
The third member says the server deadline is what ended the wait.
|
|
1459
1531
|
"""
|
|
1460
1532
|
|
|
1461
|
-
|
|
1533
|
+
started = clock()
|
|
1534
|
+
deadline = started + MAX_QUERY_WAIT_SECONDS
|
|
1535
|
+
overdue_at: float | None = None
|
|
1462
1536
|
interval = QUERY_POLL_INITIAL_SECONDS
|
|
1463
1537
|
record: dict[str, Any] = {}
|
|
1464
1538
|
read = 0
|
|
@@ -1466,12 +1540,21 @@ def _poll(
|
|
|
1466
1540
|
if attempt:
|
|
1467
1541
|
if clock() >= deadline:
|
|
1468
1542
|
break
|
|
1469
|
-
|
|
1543
|
+
if overdue_at is not None and clock() >= overdue_at:
|
|
1544
|
+
return record, read, True
|
|
1545
|
+
pause = interval
|
|
1546
|
+
if overdue_at is not None:
|
|
1547
|
+
pause = max(0.0, min(pause, overdue_at - clock()))
|
|
1548
|
+
sleep(pause)
|
|
1470
1549
|
interval = min(interval * 2, QUERY_POLL_MAX_SECONDS)
|
|
1471
1550
|
if clock() >= deadline:
|
|
1472
1551
|
break
|
|
1473
1552
|
record = client.run_query(run_id, query_id)
|
|
1474
1553
|
read += 1
|
|
1554
|
+
if overdue_at is None:
|
|
1555
|
+
window = _server_window_seconds(record)
|
|
1556
|
+
if window is not None:
|
|
1557
|
+
overdue_at = started + window + QUERY_DEADLINE_GRACE_SECONDS
|
|
1475
1558
|
if record.get("run_id") not in (None, run_id):
|
|
1476
1559
|
raise ThinLaneError(
|
|
1477
1560
|
"THIN_RESPONSE_INVALID", "Studio answered with a query belonging to another run"
|
|
@@ -1479,7 +1562,9 @@ def _poll(
|
|
|
1479
1562
|
_checkpoint_binding(record, checkpoint_id, checkpoint_content_digest)
|
|
1480
1563
|
if record.get("state") in TERMINAL_QUERY_STATES:
|
|
1481
1564
|
break
|
|
1482
|
-
|
|
1565
|
+
if overdue_at is not None and clock() >= overdue_at:
|
|
1566
|
+
return record, read, True
|
|
1567
|
+
return record, read, False
|
|
1483
1568
|
|
|
1484
1569
|
|
|
1485
1570
|
def query_exit_code(payload: Mapping[str, Any]) -> int:
|
|
@@ -1499,6 +1584,8 @@ __all__ = [
|
|
|
1499
1584
|
"MAX_QUERY_WAIT_SECONDS",
|
|
1500
1585
|
"MAX_STATEMENT_BYTES",
|
|
1501
1586
|
"PYTZ_NOT_IMPORTED",
|
|
1587
|
+
"QUERY_DEADLINE_GRACE_SECONDS",
|
|
1588
|
+
"QUERY_DEADLINE_PASSED_CODE",
|
|
1502
1589
|
"QUERY_MEMO_ENTRIES",
|
|
1503
1590
|
"QUERY_MEMO_NAME",
|
|
1504
1591
|
"QUERY_POLL_INITIAL_SECONDS",
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_reader.py
RENAMED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import hashlib
|
|
6
7
|
import re
|
|
7
8
|
from collections.abc import Mapping
|
|
8
9
|
from typing import Any
|
|
@@ -141,6 +142,7 @@ class StudioV4ReaderClient(StudioV4RunClient):
|
|
|
141
142
|
"family_version",
|
|
142
143
|
"decode_options",
|
|
143
144
|
"decode_options_json",
|
|
145
|
+
"options_digest",
|
|
144
146
|
}
|
|
145
147
|
):
|
|
146
148
|
raise ThinLaneError(
|
|
@@ -160,6 +162,30 @@ class StudioV4ReaderClient(StudioV4RunClient):
|
|
|
160
162
|
raise ThinLaneError(
|
|
161
163
|
"THIN_RESPONSE_INVALID", "Studio returned inconsistent reader JSON"
|
|
162
164
|
) from error
|
|
165
|
+
# The recipe's `reader.options_digest`: SHA-256 of the canonical JSON of the returned
|
|
166
|
+
# (default-filled) options, the exact value registration recomputes for a recipe that
|
|
167
|
+
# freezes this `decode_options`. Hashed from THIS client's canonical bytes for the object,
|
|
168
|
+
# never from Studio's string, and those bytes must equal Studio's `decode_options_json`:
|
|
169
|
+
# a string that parses equal but is spelled differently would hash to a digest
|
|
170
|
+
# registration refuses.
|
|
171
|
+
local = canonical_json_bytes(response["decode_options"])
|
|
172
|
+
if local != response["decode_options_json"].encode("utf-8"):
|
|
173
|
+
raise ThinLaneError(
|
|
174
|
+
"THIN_RESPONSE_INVALID",
|
|
175
|
+
"Studio's decode_options_json is not the canonical JSON of its decode_options, "
|
|
176
|
+
"so no options_digest can be stated for it",
|
|
177
|
+
)
|
|
178
|
+
digest = hashlib.sha256(local).hexdigest()
|
|
179
|
+
# ⚠ FORWARD-LOOKING ONLY. Studio 4.57.0 states `options_digest` when the command sets
|
|
180
|
+
# `include_options_digest: true`, but every older Studio's normalize command is
|
|
181
|
+
# `additionalProperties: false` and would 422 the flag, and this client negotiates no
|
|
182
|
+
# contract version before the call. So the flag is not sent and this cross-check runs only
|
|
183
|
+
# against a Studio that states the digest unasked.
|
|
184
|
+
if "options_digest" in response and response["options_digest"] != digest:
|
|
185
|
+
raise ThinLaneError(
|
|
186
|
+
"THIN_RESPONSE_INVALID", "Studio returned a reader options_digest for other options"
|
|
187
|
+
)
|
|
188
|
+
response["options_digest"] = digest
|
|
163
189
|
return response
|
|
164
190
|
|
|
165
191
|
def open_research_session(self, body: Mapping[str, Any]) -> dict[str, Any]:
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_search.py
RENAMED
|
@@ -1529,7 +1529,7 @@ def _workbench_read(
|
|
|
1529
1529
|
# rows: only a failure is settled from the handle alone, and every other state is polled.
|
|
1530
1530
|
if handle.get("state") != "failed":
|
|
1531
1531
|
try:
|
|
1532
|
-
record, polls = v4_query._poll(
|
|
1532
|
+
record, polls, overdue = v4_query._poll(
|
|
1533
1533
|
selected,
|
|
1534
1534
|
run_id,
|
|
1535
1535
|
query_id,
|
|
@@ -1547,6 +1547,8 @@ def _workbench_read(
|
|
|
1547
1547
|
f"{_poll_path(run_id, query_id)}. Running this same command again converges "
|
|
1548
1548
|
"on that same query rather than queueing a second",
|
|
1549
1549
|
) from None
|
|
1550
|
+
if overdue and record.get("state") not in v4_query.TERMINAL_QUERY_STATES:
|
|
1551
|
+
raise v4_query._deadline_passed(record, query_id, polls)
|
|
1550
1552
|
if record.get("query_id") not in (None, query_id):
|
|
1551
1553
|
raise ThinLaneError(
|
|
1552
1554
|
"THIN_RESPONSE_INVALID", "Studio answered the poll with another query"
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/remediation.py
RENAMED
|
@@ -11,7 +11,7 @@ neither affordable nor honest:
|
|
|
11
11
|
* **Families.** Every code prefix is assigned to a family -- 224 of them, plus 21 compound
|
|
12
12
|
prefixes, across 39 families. A family is one paragraph: what this class of failure is about,
|
|
13
13
|
where to look, and what to do. Every prefix has one, so no code can fail to say something useful.
|
|
14
|
-
* **Curated codes.** The
|
|
14
|
+
* **Curated codes.** The 121 failures a person actually meets get their own exact sentences, and
|
|
15
15
|
where one command is the whole answer, the command itself.
|
|
16
16
|
|
|
17
17
|
Those four counts are not a note somebody remembered to update:
|
|
@@ -2308,6 +2308,17 @@ CODES: dict[str, Remediation] = {
|
|
|
2308
2308
|
"account counts toward that account's monthly allowance instead.",
|
|
2309
2309
|
),
|
|
2310
2310
|
),
|
|
2311
|
+
# A web chat key refused a change (Cloud lib/sandbox-key-refusal.ts). The hosted-lane
|
|
2312
|
+
# paragraph would send it to mr-data login, which cannot help.
|
|
2313
|
+
"THIN_CLOUD_SANDBOX_KEY_READ_ONLY": Remediation(
|
|
2314
|
+
lines=(
|
|
2315
|
+
"This command ran with a web chat key. It reads, and the key is valid; changes "
|
|
2316
|
+
"go through the chat's own tools, which the person approves there, or a person "
|
|
2317
|
+
"makes them on the dashboard.",
|
|
2318
|
+
"The message names the chat tool that does this. Signing in again or replacing the "
|
|
2319
|
+
"key will not change the answer.",
|
|
2320
|
+
),
|
|
2321
|
+
),
|
|
2311
2322
|
"THIN_CLOUD_UNREACHABLE": Remediation(
|
|
2312
2323
|
lines=(
|
|
2313
2324
|
"Mostly Right Cloud did not answer: the connection was refused, cut, or timed out.",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/agents/openai.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/boundaries.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/live-run.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/one-install.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/promote.md
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/receipts.md
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/recipe-test.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/skills/mr-data-build/references/transforms.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/agent_protocol.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/canonical.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/formats.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/key_seam.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/page_coverage.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/session_probes.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/skill_assets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/table_manifest.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/acquire.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/activity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/approvals.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/backoff.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/delivery.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/download.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/narrative.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/parity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/probe.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/propose.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/research.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/router.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/stream.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/transport.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_public.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_runs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_stream.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/thin/v4_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/attendance.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/cloud_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/credentials.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/login.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/path_kind.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/plain_file.py
RENAMED
|
File without changes
|
{mostlyright_data-0.31.13 → mostlyright_data-0.31.14}/src/mostlyright/data_harness/ux/render.py
RENAMED
|
File without changes
|