mostlyright-data 0.31.8__tar.gz → 0.31.9__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.8 → mostlyright_data-0.31.9}/PKG-INFO +1 -1
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/commands.md +1 -1
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/promote.md +11 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/router.py +2 -2
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4.py +9 -19
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_artifacts.py +12 -3
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_runs.py +147 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_stream.py +26 -5
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_tables.py +276 -11
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/render.py +10 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/.gitignore +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/README.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/pyproject.toml +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/SKILL.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/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.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/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.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/agent-protocol.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/boundaries.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/enriching-with-small-models.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/installation-parity.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/live-run.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/making-a-table-incremental.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/narrating-the-run.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/one-install.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/patterns.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/prediction-labels.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/preparing-table-search.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/readers.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/receipts.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/recipe-test.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/reference-pages.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/required-protocol.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/source-credentials.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/sources.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/transforms.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/user-communication-contract.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/agent_protocol.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/table_manifest.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/backoff.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/delivery.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/description_shape.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/enrichment.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/fast_features.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/matching_benchmark.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/progress_rate.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/recipe.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/recipe_explain.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/recipe_forecast.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/recipe_test.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/refresh_rehearsal.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/release_check.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/run_diagnose.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/run_prefix.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/session.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/studio_capabilities.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/support.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/table_search_contract.json +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/table_view.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_connected.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_datasets.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_matching.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_public.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_search.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_search_contract.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/attendance.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/ux/remediation.py +0 -0
{mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/commands.md
RENAMED
|
@@ -47,7 +47,7 @@ run in full.
|
|
|
47
47
|
| `mr-data open` | Mint a single-use, short-lived address that continues this session in a browser at one dashboard path, and print it. `--open` hands it to the platform opener. The credential decides who mints: a device credential — what `mr-data login` stores — mints at Cloud, and a personal access token mints at Studio. |
|
|
48
48
|
| `mr-data promote` | Record the cadence one table refreshes on and the reasoning behind it. A table's first succeeded run goes live on its own, so this is not what makes it readable; it is idempotent on a table that is already live, and it is how a withdrawn table is put back. The Harness worker does not perform the catch-up or scheduled refresh; report those only when another deployed component returns durable evidence that it did. |
|
|
49
49
|
| `mr-data reschedule` | Change how often one live table refreshes, without the demote and promote that would reset its bookkeeping and buy a catch-up run. `--cadence` takes a cron expression or an interval such as `every 6h`; `--why` records the reasoning. It starts no run. `--lock` freezes the schedule so Studio stops adjusting it; `--unlock` lets it follow the source again. A table that cannot refresh answers `table_schedule_held` and exits 2, naming the two repairs: declare `rebuild`, or `table resync`. |
|
|
50
|
-
| `mr-data table` | Report what one table is serving and how its refresh schedule is going: the state in plain words, the schedule in force, the next refresh, the schedule that was asked for and the reason given for it, and — once Studio has settled on one — the interval it measured and how many source updates that rests on. Where Studio reports it, it also prints the table's refresh strategy: "rebuilds every 7d; next T" or "held: blocker". `table rename TABLE --name NAME` writes a new name, lowercase snake_case and unique within its dataset, fenced on that dataset's version and retried once if somebody else wrote first; nothing is rebuilt, because every run, version and schedule hangs off the identifier, which does not move. `table archive TABLE --confirm-name NAME` retires one table you name by the name it is called, and frees that name; a live table is refused until you `demote` it, and nothing built is deleted. `table archive TABLE [TABLE ...] --confirm DELETE` retires as many tables as you name at once: `DELETE` is the literal word, typed exactly, and it confirms the identifiers you typed rather than a name, so each table is retired under the name Studio is holding for it. Every table is attempted whatever the one before it answered, one line is printed for each, and the command exits non-zero if any was not retired — so read the outcome of each rather than the exit code alone. `--confirm-name` is refused when you name more than one table. `table view TABLE` says whether a live table has a saved view (`view_saved`) and lists the view kinds (`table`, `cards`, `places`, `products`, `events`, `jobs`, `people`, `articles`) and the roles each binds. There is no map or chart kind, and `places` are location cards. `table view TABLE --set VIEW.json` saves `{"version": 1, "template": KIND, "fields": {ROLE: COLUMN}}`, with optional `facts` (up to 4 columns) and `imageStyle`. `--dry-run` has Cloud validate it against the live table and save nothing. The file is checked here before anything is sent. Replacing a saved view needs its revision as `--expected-revision`, which MCP `get_table` reports, because Cloud serves no CLI read of it. A stale revision is refused `THIN_TABLE_VIEW_VIEW_CHANGED`. The command uses the device key. |
|
|
50
|
+
| `mr-data table` | Report what one table is serving and how its refresh schedule is going: the state in plain words, the schedule in force, the next refresh, the schedule that was asked for and the reason given for it, and — once Studio has settled on one — the interval it measured and how many source updates that rests on. Where Studio reports it, it also prints the table's refresh strategy: "rebuilds every 7d; next T" or "held: blocker". `table rename TABLE --name NAME` writes a new name, lowercase snake_case and unique within its dataset, fenced on that dataset's version and retried once if somebody else wrote first; nothing is rebuilt, because every run, version and schedule hangs off the identifier, which does not move. `table archive TABLE --confirm-name NAME` retires one table you name by the name it is called, and frees that name; a live table is refused until you `demote` it, and nothing built is deleted. `table archive TABLE [TABLE ...] --confirm DELETE` retires as many tables as you name at once: `DELETE` is the literal word, typed exactly, and it confirms the identifiers you typed rather than a name, so each table is retired under the name Studio is holding for it. Every table is attempted whatever the one before it answered, one line is printed for each, and the command exits non-zero if any was not retired — so read the outcome of each rather than the exit code alone. `--confirm-name` is refused when you name more than one table. `table view TABLE` says whether a live table has a saved view (`view_saved`) and lists the view kinds (`table`, `cards`, `places`, `products`, `events`, `jobs`, `people`, `articles`) and the roles each binds. There is no map or chart kind, and `places` are location cards. `table view TABLE --set VIEW.json` saves `{"version": 1, "template": KIND, "fields": {ROLE: COLUMN}}`, with optional `facts` (up to 4 columns) and `imageStyle`. `--dry-run` has Cloud validate it against the live table and save nothing. The file is checked here before anything is sent. Replacing a saved view needs its revision as `--expected-revision`, which MCP `get_table` reports, because Cloud serves no CLI read of it. A stale revision is refused `THIN_TABLE_VIEW_VIEW_CHANGED`. The command uses the device key. When the newest version is held from live, `table show` says `The newest version X is held: <reason>; serving Y`. `table accept-version TABLE --version VERSION_ID` releases that hold; live then derives from the newest unheld version. |
|
|
51
51
|
| `mr-data pin` | Ask Studio to freeze one table pointer on an exact version, for a rollback or a hold. |
|
|
52
52
|
| `mr-data unpin` | Ask Studio to resume pointer tracking; read the returned state before claiming it resumed. |
|
|
53
53
|
| `mr-data demote` | Ask Studio to withdraw the pointer and any schedule it owns, for one table or for as many as you name: `demote TABLE [TABLE ...]` withdraws them one after another, attempts every one of them whatever the one before it answered, prints a line for each, and exits non-zero if any is still live. Pair it with `table archive` when you are retiring a set: a live table cannot be archived, so it is withdraw-then-archive, two commands rather than a loop. |
|
{mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/skills/mr-data-build/references/promote.md
RENAMED
|
@@ -83,6 +83,17 @@ or a hold, `mr-data unpin TABLE_ID` resumes tracking, and `mr-data demote TABLE_
|
|
|
83
83
|
schedule and withdraws the live pointer. Read the resulting table state, but do not infer a
|
|
84
84
|
schedule, catch-up, or current bytes from the command name or from route acceptance.
|
|
85
85
|
|
|
86
|
+
**A succeeded run can be held from live.** Studio stamps every succeeded run with `verification`:
|
|
87
|
+
`verified`, or `degraded` with its reasons. When the new version would replace a better one, it
|
|
88
|
+
also stamps `live_hold`: the version is sealed and readable but not serving, and the table keeps
|
|
89
|
+
the version it had. `completeness` means the new version is degraded and the old one is verified.
|
|
90
|
+
`shrink` means it holds under half the old version's rows. `retention` means it sealed fewer rows
|
|
91
|
+
than the recipe's `verification.min_retained_ratio` allows. `mr-data status`, `watch` and
|
|
92
|
+
`receipt` say so, and `mr-data table show TABLE_ID` names the held version and the one serving.
|
|
93
|
+
Never report a held version as live. Find out why it is smaller or less complete first. If it is
|
|
94
|
+
right, `mr-data table accept-version TABLE_ID --version VERSION_ID` releases the hold. Live then
|
|
95
|
+
derives from the newest unheld version, so a newer version that has advanced since stays live.
|
|
96
|
+
|
|
86
97
|
To retire several tables, do it in two commands rather than a loop: `mr-data demote TABLE_ID
|
|
87
98
|
TABLE_ID ...` withdraws them, then `mr-data table archive TABLE_ID TABLE_ID ... --confirm DELETE`
|
|
88
99
|
retires them. `DELETE` is the literal word and nothing else is accepted; it confirms the
|
{mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/router.py
RENAMED
|
@@ -164,8 +164,8 @@ HOSTED_SUMMARY = dict(
|
|
|
164
164
|
("reschedule", "change how often one table refreshes, without starting a run"),
|
|
165
165
|
(
|
|
166
166
|
"table",
|
|
167
|
-
"report what one table is serving, explicitly resync it, rename it,
|
|
168
|
-
"many",
|
|
167
|
+
"report what one table is serving, explicitly resync it, rename it, accept a version "
|
|
168
|
+
"held from live, or retire one or many",
|
|
169
169
|
),
|
|
170
170
|
("pin", "ask Studio to pin the live pointer and report the returned state"),
|
|
171
171
|
("unpin", "ask Studio to remove a pin and report the returned pointer state"),
|
|
@@ -277,9 +277,9 @@ RUN_EVENTS_PATH = "/v4/runs/{run_id}/events"
|
|
|
277
277
|
#: bounded window of the run's log, with the sequence of the newest such event in that window. It
|
|
278
278
|
#: reads the sealed record without mutating it or its ETag.
|
|
279
279
|
#:
|
|
280
|
-
#: ⚠ DECLARED WITH NO CALLER YET
|
|
281
|
-
#:
|
|
282
|
-
#:
|
|
280
|
+
#: ⚠ DECLARED WITH NO CALLER YET. This client renders progress by following
|
|
281
|
+
#: :data:`RUN_EVENTS_PATH` and folding the frames itself, so a command that wants one bounded
|
|
282
|
+
#: snapshot rather than the stream lands against this constant.
|
|
283
283
|
RUN_PROGRESS_PATH = "/v4/runs/{run_id}/progress"
|
|
284
284
|
|
|
285
285
|
#: ``GET`` -- one row per attempt of a run, oldest first and at most 32: how it ended, when it was
|
|
@@ -462,15 +462,9 @@ ARCHIVE_TABLE_PATH = "/v4/tables/{table_id}:archive"
|
|
|
462
462
|
#: ``POST`` -- retire one whole dataset, under its typed name, and release the name it held.
|
|
463
463
|
ARCHIVE_DATASET_PATH = "/v4/datasets/{dataset_id}:archive"
|
|
464
464
|
|
|
465
|
-
#: ``GET`` -- every version ``:pin`` accepts, newest first, with the digest each one sealed
|
|
466
|
-
#:
|
|
467
|
-
#:
|
|
468
|
-
#: version the caller names, and the set of nameable versions is server-side: a caller never holds
|
|
469
|
-
#: a run, so without this route it had no way to enumerate what it was allowed to name and rollback
|
|
470
|
-
#: was reachable only by guessing. Studio's 406-03 shipped it after the whole v4 command surface
|
|
471
|
-
#: had been written, so the command that reads it lands later against exactly this constant --
|
|
472
|
-
#: which is why it is here and not in :data:`UNUSED_STUDIO_PATHS`: "no command reaches it yet" is
|
|
473
|
-
#: not a reason for never calling it.
|
|
465
|
+
#: ``GET`` -- every version ``:pin`` accepts, newest first, with the digest each one sealed and a
|
|
466
|
+
#: ``live_hold`` on each version Studio held from live. ``mr-data table show`` reads its newest row
|
|
467
|
+
#: to say whether the newest version is held.
|
|
474
468
|
LIST_TABLE_VERSIONS_PATH = "/v4/tables/{table_id}/versions"
|
|
475
469
|
|
|
476
470
|
#: ``GET`` -- one table version's manifest, projected from Studio's own part mirror rather than
|
|
@@ -765,19 +759,15 @@ DECLARED_V4_PATHS: frozenset[str] = frozenset(
|
|
|
765
759
|
# (Every line in this block narrates the promotion it sits on. What the pending set holds
|
|
766
760
|
# TODAY is stated once, above `PENDING_V4_PATHS` itself, and today it holds nothing.)
|
|
767
761
|
SESSION_HANDOFF_PATH,
|
|
768
|
-
# ⚠ AND ONE ROUTE NO COMMAND READS YET. See `LIST_TABLE_VERSIONS_PATH`: it is declared
|
|
769
|
-
# because the parity gate demands the whole public surface be accounted for, and because
|
|
770
|
-
# the reason it has no caller today is a schedule rather than a decision never to call it.
|
|
771
762
|
LIST_TABLE_VERSIONS_PATH,
|
|
772
763
|
# ⚠ AND THE THREE PARTITIONED-TABLE ROUTES, promoted out of `PENDING_V4_PATHS` once the
|
|
773
764
|
# parity gate proved Studio had been serving them all along. They were recorded as pending
|
|
774
765
|
# under the placeholder name `{version}`; the contract spells it `{table_version_id}`, so
|
|
775
766
|
# the entries asserted the absence of paths that do not exist and were true for the wrong
|
|
776
767
|
# reason. `mr-data parts` calls the manifest, and `mr-data download` calls the session
|
|
777
|
-
# minting for every part of a version its own run did not seal. The export is declared
|
|
778
|
-
# the
|
|
779
|
-
#
|
|
780
|
-
# anything calls it yet.
|
|
768
|
+
# minting for every part of a version its own run did not seal. The export is declared
|
|
769
|
+
# because the parity gate accounts for the whole public surface, and its caller lands
|
|
770
|
+
# against exactly this constant -- not because anything calls it yet.
|
|
781
771
|
TABLE_VERSION_MANIFEST_PATH,
|
|
782
772
|
TABLE_VERSION_PARTS_SESSIONS_PATH,
|
|
783
773
|
TABLE_VERSION_EXPORT_PATH,
|
|
@@ -97,6 +97,7 @@ from mostlyright.data_harness.thin.v4_runs import (
|
|
|
97
97
|
StudioV4RunClient,
|
|
98
98
|
failure_from,
|
|
99
99
|
failure_sentence,
|
|
100
|
+
verification_members,
|
|
100
101
|
)
|
|
101
102
|
from mostlyright.data_harness.thin.v4_stream import check_results, terminal_event
|
|
102
103
|
from mostlyright.data_harness.ux.credentials import (
|
|
@@ -857,8 +858,9 @@ def _head(
|
|
|
857
858
|
"""The three facts every answer in this module opens with.
|
|
858
859
|
|
|
859
860
|
The address is the run's Dataset page when the command read the run record, and the team's
|
|
860
|
-
Dataset listing when it did not: ``download
|
|
861
|
-
|
|
861
|
+
Dataset listing when it did not: ``download`` and ``peek`` read nothing but artifacts, so they
|
|
862
|
+
hold no Dataset identifier to name a page with. ``receipt`` reads the run record and passes it
|
|
863
|
+
when the read succeeds.
|
|
862
864
|
"""
|
|
863
865
|
|
|
864
866
|
return {
|
|
@@ -1670,14 +1672,21 @@ def receipt(
|
|
|
1670
1672
|
# receipt's own `artifact_id` is on the payload, so which one was read is never a guess.
|
|
1671
1673
|
row = _rows_of_kind(_listing(selected, run_id), ("receipt",), run_id=run_id)[0]
|
|
1672
1674
|
document = _document(selected, run_id, row)
|
|
1675
|
+
# The sealed receipt says how the run was built; whether Studio found its version complete,
|
|
1676
|
+
# and whether that version is serving, is on the run record. A refused read leaves it out.
|
|
1677
|
+
try:
|
|
1678
|
+
record: Mapping[str, Any] | None = selected.run(run_id)
|
|
1679
|
+
except ThinLaneError:
|
|
1680
|
+
record = None
|
|
1673
1681
|
return {
|
|
1674
1682
|
"schema_version": RECEIPT_SCHEMA,
|
|
1675
1683
|
"status": "receipt_reported",
|
|
1676
|
-
**_head(selected.session, run_id),
|
|
1684
|
+
**_head(selected.session, run_id, record),
|
|
1677
1685
|
"artifact_id": _artifact_id(row),
|
|
1678
1686
|
"digest": row.get("digest"),
|
|
1679
1687
|
"size_bytes": row.get("size_bytes"),
|
|
1680
1688
|
"receipt": document,
|
|
1689
|
+
**(verification_members(record) if record is not None else {}),
|
|
1681
1690
|
}
|
|
1682
1691
|
|
|
1683
1692
|
|
{mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_runs.py
RENAMED
|
@@ -2739,6 +2739,7 @@ def status(args: argparse.Namespace, *, client: StudioV4RunClient | None = None)
|
|
|
2739
2739
|
)
|
|
2740
2740
|
if delivered is not None:
|
|
2741
2741
|
payload["delivery"] = delivered
|
|
2742
|
+
payload.update(verification_members(run))
|
|
2742
2743
|
if diagnosing:
|
|
2743
2744
|
# Imported here because the diagnosis reads this module's triple and room-fault reading,
|
|
2744
2745
|
# and a module-level import in both directions is a cycle.
|
|
@@ -2860,6 +2861,144 @@ def status_exit_code(payload: Mapping[str, Any]) -> int:
|
|
|
2860
2861
|
return 2 if payload.get("run", {}).get("status") == "failed" else 0
|
|
2861
2862
|
|
|
2862
2863
|
|
|
2864
|
+
# --------------------------------------------------------------------------------------------
|
|
2865
|
+
# Verification and the live hold
|
|
2866
|
+
# --------------------------------------------------------------------------------------------
|
|
2867
|
+
|
|
2868
|
+
|
|
2869
|
+
#: The run-record members Studio stamps on a succeeded run about whether its version is complete
|
|
2870
|
+
#: and whether it became live. Carried verbatim; absent on a record written before they existed.
|
|
2871
|
+
VERIFICATION_MEMBERS: tuple[str, ...] = ("verification", "live_hold", "live_hold_accepted")
|
|
2872
|
+
|
|
2873
|
+
#: What each ``verification.reasons`` token means, in the order Studio lists them.
|
|
2874
|
+
DEGRADED_REASONS: dict[str, str] = {
|
|
2875
|
+
"ROW_CLAMP_TRUNCATED": "a row clamp cut the table short",
|
|
2876
|
+
"DISCOVERY_INCOMPLETE": "a page collection's listing stopped before its end",
|
|
2877
|
+
"DISCOVERY_FAILED": "a page collection's listing was refused",
|
|
2878
|
+
"PAGES_FAILED": "pages whose last fetch failed are missing",
|
|
2879
|
+
"SOURCE_COUNT_SHORT": "a source delivered fewer rows or pages than it states it holds",
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
#: What each ``live_hold.reason`` means for the version it is stamped on.
|
|
2883
|
+
LIVE_HOLD_REASONS: dict[str, str] = {
|
|
2884
|
+
"completeness": "it is degraded and the version it would have replaced is verified",
|
|
2885
|
+
"shrink": "it holds fewer than half the rows of the version it would have replaced",
|
|
2886
|
+
"retention": (
|
|
2887
|
+
"it sealed a smaller share of the rows its sources supplied than the recipe's "
|
|
2888
|
+
"verification.min_retained_ratio allows"
|
|
2889
|
+
),
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
|
|
2893
|
+
def accept_version_command(table_id: Any, version_id: Any) -> str:
|
|
2894
|
+
"""The command that releases one held version."""
|
|
2895
|
+
|
|
2896
|
+
return shlex.join(
|
|
2897
|
+
[
|
|
2898
|
+
"mr-data",
|
|
2899
|
+
"table",
|
|
2900
|
+
"accept-version",
|
|
2901
|
+
str(table_id or "TABLE_ID"),
|
|
2902
|
+
"--version",
|
|
2903
|
+
str(version_id or "VERSION_ID"),
|
|
2904
|
+
]
|
|
2905
|
+
)
|
|
2906
|
+
|
|
2907
|
+
|
|
2908
|
+
def live_hold_reason(reason: Any) -> str:
|
|
2909
|
+
"""One hold reason in words, or the token itself when this client does not know it."""
|
|
2910
|
+
|
|
2911
|
+
if reason is None:
|
|
2912
|
+
return "Studio held it without naming a reason"
|
|
2913
|
+
return LIVE_HOLD_REASONS.get(
|
|
2914
|
+
str(reason), f"Studio held it for a reason this client does not know ({reason})"
|
|
2915
|
+
)
|
|
2916
|
+
|
|
2917
|
+
|
|
2918
|
+
def _tokens(value: Any) -> list[str]:
|
|
2919
|
+
"""The string members of a list, or nothing when the member is not a list."""
|
|
2920
|
+
|
|
2921
|
+
if not isinstance(value, list):
|
|
2922
|
+
return []
|
|
2923
|
+
return [item for item in value if isinstance(item, str) and item]
|
|
2924
|
+
|
|
2925
|
+
|
|
2926
|
+
def verification_sentence(verification: Mapping[str, Any]) -> str:
|
|
2927
|
+
"""``verified`` or ``degraded`` with the reasons, and any warning, as one sentence."""
|
|
2928
|
+
|
|
2929
|
+
state = verification.get("status")
|
|
2930
|
+
reasons = _tokens(verification.get("reasons"))
|
|
2931
|
+
if state == "degraded":
|
|
2932
|
+
said = "Degraded: the run succeeded but the version is short of what its sources hold"
|
|
2933
|
+
if reasons:
|
|
2934
|
+
said += (
|
|
2935
|
+
" ("
|
|
2936
|
+
+ "; ".join(
|
|
2937
|
+
f"{reason}: {DEGRADED_REASONS.get(reason, reason)}" for reason in reasons
|
|
2938
|
+
)
|
|
2939
|
+
+ ")"
|
|
2940
|
+
)
|
|
2941
|
+
said += "."
|
|
2942
|
+
elif state == "verified":
|
|
2943
|
+
said = "Verified: Studio found nothing missing from this version."
|
|
2944
|
+
else:
|
|
2945
|
+
said = f"Studio reports verification {state!r}."
|
|
2946
|
+
sealed, acquired = verification.get("sealed_rows"), verification.get("acquired_rows")
|
|
2947
|
+
for warning in _tokens(verification.get("warnings")):
|
|
2948
|
+
if warning == "SEALED_BELOW_ACQUIRED":
|
|
2949
|
+
said += " Warning SEALED_BELOW_ACQUIRED: the table holds under a tenth of the rows "
|
|
2950
|
+
said += "its sources supplied"
|
|
2951
|
+
if isinstance(sealed, int) and isinstance(acquired, int):
|
|
2952
|
+
said += f" ({sealed:,} of {acquired:,})"
|
|
2953
|
+
said += "."
|
|
2954
|
+
else:
|
|
2955
|
+
said += f" Warning {warning}."
|
|
2956
|
+
return said
|
|
2957
|
+
|
|
2958
|
+
|
|
2959
|
+
def live_hold_sentence(run: Mapping[str, Any]) -> str | None:
|
|
2960
|
+
"""What a held version means for the table, and the command that releases it."""
|
|
2961
|
+
|
|
2962
|
+
hold = run.get("live_hold")
|
|
2963
|
+
if isinstance(hold, Mapping):
|
|
2964
|
+
version = run.get("table_version_id")
|
|
2965
|
+
named = f"Version {version}" if version else "This version"
|
|
2966
|
+
return (
|
|
2967
|
+
f"{named} is sealed but not serving: Studio held it from live because "
|
|
2968
|
+
f"{live_hold_reason(hold.get('reason'))}. Live kept version "
|
|
2969
|
+
f"{hold.get('against_version_id')}, the one it would have replaced; "
|
|
2970
|
+
f"mr-data table show {run.get('table_id') or 'TABLE_ID'} says what the table serves "
|
|
2971
|
+
f"now. If this version is right, accept it with "
|
|
2972
|
+
f"{accept_version_command(run.get('table_id'), version)}."
|
|
2973
|
+
)
|
|
2974
|
+
accepted = run.get("live_hold_accepted")
|
|
2975
|
+
if isinstance(accepted, Mapping):
|
|
2976
|
+
return (
|
|
2977
|
+
f"This version was held from live ({accepted.get('reason')}) and accepted at "
|
|
2978
|
+
f"{accepted.get('accepted_at')}."
|
|
2979
|
+
)
|
|
2980
|
+
return None
|
|
2981
|
+
|
|
2982
|
+
|
|
2983
|
+
def verification_members(run: Mapping[str, Any]) -> dict[str, Any]:
|
|
2984
|
+
"""The verification and live-hold members a run receipt carries, or nothing.
|
|
2985
|
+
|
|
2986
|
+
The record's own members travel verbatim; the two sentences are what a person reads. A record
|
|
2987
|
+
written before Studio stamped them yields an empty mapping, so older runs print as before.
|
|
2988
|
+
"""
|
|
2989
|
+
|
|
2990
|
+
members: dict[str, Any] = {
|
|
2991
|
+
member: run[member] for member in VERIFICATION_MEMBERS if isinstance(run.get(member), dict)
|
|
2992
|
+
}
|
|
2993
|
+
verification = members.get("verification")
|
|
2994
|
+
if isinstance(verification, Mapping):
|
|
2995
|
+
members["verification_said"] = verification_sentence(verification)
|
|
2996
|
+
held = live_hold_sentence(run)
|
|
2997
|
+
if held is not None:
|
|
2998
|
+
members["live_hold_said"] = held
|
|
2999
|
+
return members
|
|
3000
|
+
|
|
3001
|
+
|
|
2863
3002
|
# --------------------------------------------------------------------------------------------
|
|
2864
3003
|
# Listing them
|
|
2865
3004
|
# --------------------------------------------------------------------------------------------
|
|
@@ -3176,11 +3315,13 @@ def _matches(
|
|
|
3176
3315
|
__all__ = [
|
|
3177
3316
|
"CONFIRM_REQUIRED_CODE",
|
|
3178
3317
|
"DEFAULT_RUN_LIMIT",
|
|
3318
|
+
"DEGRADED_REASONS",
|
|
3179
3319
|
"DISCARD_HISTORY_FLAG",
|
|
3180
3320
|
"FAILURE_MEMBERS",
|
|
3181
3321
|
"FRESH_READ_CONTROL",
|
|
3182
3322
|
"GRAMMAR_NOT_SERVED_CODE",
|
|
3183
3323
|
"LIST_PAGE_SIZE",
|
|
3324
|
+
"LIVE_HOLD_REASONS",
|
|
3184
3325
|
"MAX_CLAMP_BYTES",
|
|
3185
3326
|
"MAX_PAGE_HALVINGS",
|
|
3186
3327
|
"MAX_RUN_PAGES",
|
|
@@ -3202,9 +3343,11 @@ __all__ = [
|
|
|
3202
3343
|
"STEP_UP_CODES",
|
|
3203
3344
|
"TERMINAL_RUN_STATUSES",
|
|
3204
3345
|
"TEST_STOPPED_STATUSES",
|
|
3346
|
+
"VERIFICATION_MEMBERS",
|
|
3205
3347
|
"WINDOW_MEMBERS",
|
|
3206
3348
|
"AdaptivePage",
|
|
3207
3349
|
"StudioV4RunClient",
|
|
3350
|
+
"accept_version_command",
|
|
3208
3351
|
"approve_full",
|
|
3209
3352
|
"confirm_held",
|
|
3210
3353
|
"confirm_held_command",
|
|
@@ -3219,6 +3362,8 @@ __all__ = [
|
|
|
3219
3362
|
"fresh_full_record",
|
|
3220
3363
|
"is_room_fault",
|
|
3221
3364
|
"listing_page_size",
|
|
3365
|
+
"live_hold_reason",
|
|
3366
|
+
"live_hold_sentence",
|
|
3222
3367
|
"pages_sentence",
|
|
3223
3368
|
"registered_digest",
|
|
3224
3369
|
"release_full_command",
|
|
@@ -3236,4 +3381,6 @@ __all__ = [
|
|
|
3236
3381
|
"status",
|
|
3237
3382
|
"status_exit_code",
|
|
3238
3383
|
"table_capabilities",
|
|
3384
|
+
"verification_members",
|
|
3385
|
+
"verification_sentence",
|
|
3239
3386
|
]
|
{mostlyright_data-0.31.8 → mostlyright_data-0.31.9}/src/mostlyright/data_harness/thin/v4_stream.py
RENAMED
|
@@ -62,6 +62,7 @@ from mostlyright.data_harness.thin.v4_runs import (
|
|
|
62
62
|
_preflight_run,
|
|
63
63
|
failure_from,
|
|
64
64
|
failure_sentence,
|
|
65
|
+
verification_members,
|
|
65
66
|
)
|
|
66
67
|
from mostlyright.data_harness.ux.credentials import resolve_cloud_credentials
|
|
67
68
|
|
|
@@ -246,11 +247,12 @@ def watch(
|
|
|
246
247
|
live view is that it is live -- and the returned payload is the summary, so ``--json`` gets one
|
|
247
248
|
object and a person gets a timeline followed by that object.
|
|
248
249
|
|
|
249
|
-
⚠ THE RUN RECORD IS READ
|
|
250
|
-
because it names the Dataset whose page this run is followed on. The terminal event says
|
|
251
|
-
happened and carries the whole failure triple, so a
|
|
252
|
-
authority the question the stream just answered
|
|
253
|
-
|
|
250
|
+
⚠ THE RUN RECORD IS READ BEFORE THE TIMELINE, AND AFTERWARDS ONLY FOR A SUCCESS. It is read
|
|
251
|
+
first because it names the Dataset whose page this run is followed on. The terminal event says
|
|
252
|
+
what happened and carries the whole failure triple, so a failed run is not read again: a second
|
|
253
|
+
GET would be asking a second authority the question the stream just answered. A succeeded run
|
|
254
|
+
is read once more for its ``verification`` and ``live_hold``, which the terminal event does
|
|
255
|
+
not carry.
|
|
254
256
|
|
|
255
257
|
⚠ THE STREAM READS THROUGH ``sessions`` AND THE ONE RECORD READ DOES NOT NEED TO. The record
|
|
256
258
|
is read at once, on the token just minted; the stream is what outlives it, so the stream is
|
|
@@ -341,9 +343,28 @@ def watch(
|
|
|
341
343
|
)
|
|
342
344
|
if delivered is not None:
|
|
343
345
|
payload["delivery"] = delivered
|
|
346
|
+
if terminal.get("event_type") == "run_succeeded":
|
|
347
|
+
payload.update(_succeeded_verification(current, selected, run_id))
|
|
344
348
|
return payload
|
|
345
349
|
|
|
346
350
|
|
|
351
|
+
def _succeeded_verification(
|
|
352
|
+
current: StudioSession, selected: StudioV4RunClient, run_id: str
|
|
353
|
+
) -> dict[str, Any]:
|
|
354
|
+
"""The verification and live-hold members of a run that has just succeeded, or nothing.
|
|
355
|
+
|
|
356
|
+
The terminal event does not carry them: Studio stamps them on the run record in the same
|
|
357
|
+
write, so the record is read once more after ``run_succeeded``. A refused read leaves them
|
|
358
|
+
out rather than failing a watch whose run did succeed.
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
record = StudioV4RunClient(current, transport=selected.transport).run(run_id)
|
|
363
|
+
except ThinLaneError:
|
|
364
|
+
return {}
|
|
365
|
+
return verification_members(record) if isinstance(record, Mapping) else {}
|
|
366
|
+
|
|
367
|
+
|
|
347
368
|
def watch_many(
|
|
348
369
|
args: argparse.Namespace,
|
|
349
370
|
*,
|