mostlyright-data 0.19.4__tar.gz → 0.19.5__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.19.4 → mostlyright_data-0.19.5}/PKG-INFO +1 -1
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/pyproject.toml +1 -1
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/skills/mr-data-build/SKILL.md +1 -1
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/router.py +10 -2
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/session.py +3 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_datasets.py +86 -5
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/.gitignore +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/README.md +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/table_manifest.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/recipe.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/attendance.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/remediation.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/render.py +0 -0
|
@@ -155,7 +155,7 @@ gap rather than doing anything, and `export-hosted-candidate`, which is a backen
|
|
|
155
155
|
| `mr-data clarify` | Say whether one clarifying question may be put to the user right now, and compose the record of their answer. It reaches nothing, asks nobody and waits for nothing. Exits non-zero when no person can answer or when a recipe is already registered. |
|
|
156
156
|
| `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. **Do not plan a source inspection around it.** Both positionals are identifiers — `QUESTION_ID` is a question identifier, which no v4 registration receipt returns — and the command rides the frozen `/v3/sessions` routes, which a deployment may have switched off. Read a source instead by registering the recipe and taking an unwindowed sample under `--max-rows`, then `peek`, `query` and `receipt`. |
|
|
157
157
|
| `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. |
|
|
158
|
-
| `mr-data dataset` | Bring the dataset page into existence before there is anything on it, then fill it in while somebody watches. `dataset create --name TEXT` mints it and prints the `dataset_id`; `dataset show ID` reads it back; `dataset set ID --name TEXT --topics "a,b,c" --license ID --description-file F` writes the title, the descriptive tags, the SPDX licence and the description under the version it was read at, retrying once if somebody else wrote first, and an empty `--topics` or `--license` takes that value off the page; `dataset note ID --heading H --blocks-file B` writes one cell of the decision record that OUTLIVES every run, and `--list` reads it back; `dataset watch ID` follows the page's own event stream; `dataset activity ID --phase P --message TEXT` says what is happening right now, silently, and is never a chat message and never a cell; `dataset publish ID [--mode public|link|private]` says who can read the dataset — `public` lists it in the public directory and serves it at an address anybody can read, `link` serves it at an unlisted address, `private` takes it back to the workspace — and `dataset publish ID --show` reads that back without changing it; `dataset archive ID --confirm-name TITLE` retires the page and frees its title, deleting nothing. |
|
|
158
|
+
| `mr-data dataset` | Bring the dataset page into existence before there is anything on it, then fill it in while somebody watches. `dataset create --name TEXT` mints it and prints the `dataset_id`; `dataset show ID` reads it back; `dataset set ID --name TEXT --topics "a,b,c" --license ID --description-file F` writes the title, the descriptive tags, the SPDX licence and the description under the version it was read at, retrying once if somebody else wrote first, and an empty `--topics` or `--license` takes that value off the page; a saved write whose public sync fails exits 2 and reports `public_projection_synced: false` — use `dataset sync ID` to retry that sync without rewriting Studio; `dataset note ID --heading H --blocks-file B` writes one cell of the decision record that OUTLIVES every run, and `--list` reads it back; `dataset watch ID` follows the page's own event stream; `dataset activity ID --phase P --message TEXT` says what is happening right now, silently, and is never a chat message and never a cell; `dataset publish ID [--mode public|link|private]` says who can read the dataset — `public` lists it in the public directory and serves it at an address anybody can read, `link` serves it at an unlisted address, `private` takes it back to the workspace — and `dataset publish ID --show` reads that back without changing it; `dataset archive ID --confirm-name TITLE` retires the page and frees its title, deleting nothing. |
|
|
159
159
|
| `mr-data recipe` | Register one recipe document — dataset, question, table plan, sources, transform, checks and units — in one call, and print the identifiers the server derived. `mr-data recipe show ID` reads one back. |
|
|
160
160
|
| `mr-data cover` | Generate and attach one branded 1200×630 dataset cover. Give it the dataset ID and what the image should depict; Studio fixes the model, single-color style, curated random palette, dimensions and storage. |
|
|
161
161
|
| `mr-data run` | Start one run against a registered recipe, named as `--recipe RECIPE_ID --digest RECIPE_DIGEST` — both required, neither positional, and the digest is the bare hex the registration receipt printed. The mode is one of six: `sample`, `full`, `refresh`, `backfill`, `compact` and `replay`. Four have a shorthand flag (`--sample`, `--full`, `--refresh`, `--backfill`) and two do not, so write `--mode MODE`, which is accepted for every one of them. A refresh narrows each source that declares a supported address window and merges its returned relation into retained source state; unwindowed sources are conditionally revalidated or re-acquired whole, then the full declared transform runs. `--backfill` states the exact window with `--window START END`. `--mode replay --sources-from RUN_ID` asks Studio to run the registered revision against the RETAINED RAW INPUTS of one named successful run of the same table: nothing is fetched from this computer, nothing is acquired again, the result is compared against that run and never becomes the live version, and it neither asks for nor records an approval. Where Studio has replay switched off, or the named run is not successful, not the same table, or no longer retains its inputs, it answers a typed refusal — report the code rather than retrying in another mode. `--sources-from` on any other mode is refused `THIN_ARGUMENT_INVALID` before anything is sent. `--max-rows` and `--max-source-bytes` bound ONE SOURCE rather than the finished table. A large run is held for a spend confirmation, which `--confirm` settles and whose printed `confirm_command` re-states every argument the request carried; a sample-first held full requires Studio's interactive human approval instead. `--cancel RUN` stops one. |
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/router.py
RENAMED
|
@@ -461,7 +461,7 @@ def _exit_codes() -> dict[str, Callable[[dict[str, Any]], int]]:
|
|
|
461
461
|
``ux.commands.CommandSpec`` gives: an exit code that can disagree with the printed answer is
|
|
462
462
|
an exit code somebody will trust over the answer.
|
|
463
463
|
|
|
464
|
-
|
|
464
|
+
These are answers a script acts on rather than reports it reads: a run
|
|
465
465
|
held at the spend gate has not started, a run that failed is not a build whichever surface it
|
|
466
466
|
was learned from, a table whose declared checks did not all pass is not one to go on with, a
|
|
467
467
|
question this command stopped waiting on has not been answered, and a probe that settled
|
|
@@ -476,7 +476,14 @@ def _exit_codes() -> dict[str, Callable[[dict[str, Any]], int]]:
|
|
|
476
476
|
"""
|
|
477
477
|
|
|
478
478
|
from mostlyright.data_harness.thin import probe as probe_module
|
|
479
|
-
from mostlyright.data_harness.thin import
|
|
479
|
+
from mostlyright.data_harness.thin import (
|
|
480
|
+
v4_artifacts,
|
|
481
|
+
v4_datasets,
|
|
482
|
+
v4_query,
|
|
483
|
+
v4_runs,
|
|
484
|
+
v4_stream,
|
|
485
|
+
v4_tables,
|
|
486
|
+
)
|
|
480
487
|
|
|
481
488
|
return {
|
|
482
489
|
"run": v4_runs.run_exit_code,
|
|
@@ -488,6 +495,7 @@ def _exit_codes() -> dict[str, Callable[[dict[str, Any]], int]]:
|
|
|
488
495
|
"probe": probe_module.probe_exit_code,
|
|
489
496
|
"table": v4_tables.lifecycle_exit_code,
|
|
490
497
|
"demote": v4_tables.lifecycle_exit_code,
|
|
498
|
+
"dataset": v4_datasets.dataset_exit_code,
|
|
491
499
|
}
|
|
492
500
|
|
|
493
501
|
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/session.py
RENAMED
|
@@ -63,6 +63,9 @@ CLOUD_SESSION_HANDOFF_PATH = "/api/cli/session-handoffs"
|
|
|
63
63
|
#: this client sends a mode and renders what came back rather than deciding any part of it.
|
|
64
64
|
CLOUD_DATASET_ACCESS_PATH = "/api/cli/datasets/{dataset_id}/access"
|
|
65
65
|
|
|
66
|
+
# Projection repair after a Studio metadata write, without a publication mutation.
|
|
67
|
+
CLOUD_DATASET_IDENTITY_PATH = "/api/cli/datasets/{dataset_id}/identity"
|
|
68
|
+
|
|
66
69
|
#: The one Cloud address a Dataset has. ``mostlyright-cloud`` serves it at this route with
|
|
67
70
|
#: section suffixes, and a run has no page of its own: its evidence lives on this page's Harness
|
|
68
71
|
#: card, so a receipt names its run with :func:`run_dashboard_url` as a query member here rather
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_datasets.py
RENAMED
|
@@ -117,6 +117,7 @@ from mostlyright.data_harness.thin.narrative import (
|
|
|
117
117
|
from mostlyright.data_harness.thin.runs import new_idempotency_key
|
|
118
118
|
from mostlyright.data_harness.thin.session import (
|
|
119
119
|
CLOUD_DATASET_ACCESS_PATH,
|
|
120
|
+
CLOUD_DATASET_IDENTITY_PATH,
|
|
120
121
|
CLOUD_REFUSAL_CODE,
|
|
121
122
|
MAX_STATED_MESSAGE_CHARS,
|
|
122
123
|
CloudClient,
|
|
@@ -152,7 +153,7 @@ from mostlyright.data_harness.thin.v4_narrative import (
|
|
|
152
153
|
refuse,
|
|
153
154
|
v4_cell_receipt,
|
|
154
155
|
)
|
|
155
|
-
from mostlyright.data_harness.ux.credentials import resolve_cloud_credentials
|
|
156
|
+
from mostlyright.data_harness.ux.credentials import LoginError, resolve_cloud_credentials
|
|
156
157
|
from mostlyright.data_harness.ux.plain_file import PlainFileRefusal, read_plain_file
|
|
157
158
|
|
|
158
159
|
CREATE_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-dataset-created.v1"
|
|
@@ -191,6 +192,7 @@ ACTIONS: tuple[str, ...] = (
|
|
|
191
192
|
"categories",
|
|
192
193
|
"create",
|
|
193
194
|
"set",
|
|
195
|
+
"sync",
|
|
194
196
|
"draft",
|
|
195
197
|
"show",
|
|
196
198
|
"state",
|
|
@@ -205,6 +207,7 @@ ACTIONS: tuple[str, ...] = (
|
|
|
205
207
|
#: what produces one.
|
|
206
208
|
_NEEDS_DATASET: tuple[str, ...] = (
|
|
207
209
|
"set",
|
|
210
|
+
"sync",
|
|
208
211
|
"draft",
|
|
209
212
|
"show",
|
|
210
213
|
"state",
|
|
@@ -353,13 +356,23 @@ CLOUD_COMPOSED_REFUSAL = "step_up_required"
|
|
|
353
356
|
|
|
354
357
|
|
|
355
358
|
class CloudDatasetAccessClient(CloudClient):
|
|
356
|
-
"""
|
|
359
|
+
"""Dataset access and identity routes over the shared device-key Cloud client.
|
|
357
360
|
|
|
358
361
|
Adds two routes and no transport, for the reason :class:`StudioV4DatasetClient` gives about
|
|
359
362
|
its own: a second ``call`` would be a second place for the origin check, the byte ceiling, the
|
|
360
363
|
credential header and the refusal folding to drift.
|
|
361
364
|
"""
|
|
362
365
|
|
|
366
|
+
def sync_dataset_identity(self, dataset_id: str) -> dict[str, Any]:
|
|
367
|
+
"""Carry Studio's current metadata into Cloud's existing public projection."""
|
|
368
|
+
|
|
369
|
+
return self.call(
|
|
370
|
+
"POST",
|
|
371
|
+
CLOUD_DATASET_IDENTITY_PATH.format(dataset_id=dataset_id),
|
|
372
|
+
act="update the dataset's public page",
|
|
373
|
+
body={},
|
|
374
|
+
)
|
|
375
|
+
|
|
363
376
|
def dataset_access(self, dataset_id: str) -> dict[str, Any]:
|
|
364
377
|
"""Report how this dataset is shared now, changing nothing."""
|
|
365
378
|
|
|
@@ -551,7 +564,8 @@ def declare_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
551
564
|
"Bring a dataset page into existence before there is anything on it, then fill it in "
|
|
552
565
|
"while somebody watches. `create` mints the page from a working title; `show` reads it "
|
|
553
566
|
"back; `set` writes the final title, the topics, the licence and the description, "
|
|
554
|
-
"conditional on the version it was read at; `
|
|
567
|
+
"conditional on the version it was read at; `sync` retries only public-page sync; "
|
|
568
|
+
"`note` writes one cell of the decision "
|
|
555
569
|
"record that outlives every run; `activity` says in one line what is happening right "
|
|
556
570
|
"now, which the page shows while it happens and forgets afterwards; `watch` follows the "
|
|
557
571
|
"page's own event stream; `publish` says who can read the dataset -- anybody, whoever "
|
|
@@ -1148,6 +1162,49 @@ def draft(
|
|
|
1148
1162
|
}
|
|
1149
1163
|
|
|
1150
1164
|
|
|
1165
|
+
def _sync_written_identity(dataset_id: str, cloud_url: str) -> dict[str, Any]:
|
|
1166
|
+
"""Report partial success honestly: a saved Studio write must not be lost in a sync error."""
|
|
1167
|
+
|
|
1168
|
+
try:
|
|
1169
|
+
credentials = resolve_cloud_credentials()
|
|
1170
|
+
require_device_key(credentials)
|
|
1171
|
+
cloud = CloudDatasetAccessClient(credentials)
|
|
1172
|
+
if cloud.cloud_url != cloud_url.rstrip("/"):
|
|
1173
|
+
raise ThinLaneError(
|
|
1174
|
+
"THIN_CLOUD_ORIGIN_CHANGED", "Cloud origin changed during the write"
|
|
1175
|
+
)
|
|
1176
|
+
answer = cloud.sync_dataset_identity(dataset_id)
|
|
1177
|
+
if (
|
|
1178
|
+
answer.get("schema_version") != "mostlyright-dataset-identity-sync.v1"
|
|
1179
|
+
or answer.get("status") != "synced"
|
|
1180
|
+
or answer.get("dataset_id") != dataset_id
|
|
1181
|
+
or "dataset_version" not in answer
|
|
1182
|
+
or (
|
|
1183
|
+
answer["dataset_version"] is not None
|
|
1184
|
+
and (type(answer["dataset_version"]) is not int or answer["dataset_version"] < 1)
|
|
1185
|
+
)
|
|
1186
|
+
or "dataset_slug" not in answer
|
|
1187
|
+
or (answer["dataset_slug"] is not None and not isinstance(answer["dataset_slug"], str))
|
|
1188
|
+
):
|
|
1189
|
+
raise ThinLaneError(
|
|
1190
|
+
"THIN_IDENTITY_SYNC_INVALID", "Cloud did not confirm the projection"
|
|
1191
|
+
)
|
|
1192
|
+
return {"public_projection_synced": True}
|
|
1193
|
+
except (ThinLaneError, LoginError, OSError) as error:
|
|
1194
|
+
return {
|
|
1195
|
+
"public_projection_synced": False,
|
|
1196
|
+
"public_projection_error": (
|
|
1197
|
+
error.code
|
|
1198
|
+
if isinstance(error, ThinLaneError) and getattr(error, "http_status", None) != 404
|
|
1199
|
+
else "THIN_IDENTITY_SYNC_UNAVAILABLE"
|
|
1200
|
+
),
|
|
1201
|
+
"public_projection_note": (
|
|
1202
|
+
"The dataset was saved, but its public page could not be updated. "
|
|
1203
|
+
f"Retry only the sync with: mr-data dataset sync {dataset_id}"
|
|
1204
|
+
),
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
|
|
1151
1208
|
def write(
|
|
1152
1209
|
args: argparse.Namespace,
|
|
1153
1210
|
*,
|
|
@@ -1242,8 +1299,14 @@ def write(
|
|
|
1242
1299
|
f"{version}; somebody else is editing it. Read it with mr-data dataset show "
|
|
1243
1300
|
f"{dataset_id} and write again",
|
|
1244
1301
|
) from second
|
|
1302
|
+
# Validate the write receipt before making a second request with another credential.
|
|
1303
|
+
written_id = _dataset_id(updated, verb="write")
|
|
1304
|
+
if written_id != dataset_id:
|
|
1305
|
+
raise ThinLaneError("THIN_DATASET_COORDINATE_MISMATCH", "Studio returned another dataset")
|
|
1306
|
+
projection = _sync_written_identity(dataset_id, selected.session.cloud_url)
|
|
1245
1307
|
return {
|
|
1246
1308
|
"schema_version": SET_SCHEMA,
|
|
1309
|
+
**projection,
|
|
1247
1310
|
"status": "dataset_updated",
|
|
1248
1311
|
"lane": "hosted",
|
|
1249
1312
|
"dataset_id": _dataset_id(updated, verb="write"),
|
|
@@ -1598,6 +1661,7 @@ def note(
|
|
|
1598
1661
|
#: belongs to both; that is one verb short of the whole truth and still points at the pair of
|
|
1599
1662
|
#: commands the flag is for, which is what somebody who mistyped needs.
|
|
1600
1663
|
_VERB_FLAGS: Mapping[str, tuple[tuple[str, str], ...]] = {
|
|
1664
|
+
"sync": (),
|
|
1601
1665
|
"set": (("name", "--name"), ("description_file", "--description-file")),
|
|
1602
1666
|
"note": (
|
|
1603
1667
|
("heading", "--heading"),
|
|
@@ -1923,6 +1987,18 @@ def dataset(
|
|
|
1923
1987
|
return draft(args, client=client, stdin=stdin)
|
|
1924
1988
|
if action == "set":
|
|
1925
1989
|
return write(args, client=client, stdin=stdin)
|
|
1990
|
+
if action == "sync":
|
|
1991
|
+
dataset_id = identifier(args.dataset_id, "dataset identifier")
|
|
1992
|
+
credentials = resolve_cloud_credentials()
|
|
1993
|
+
result = _sync_written_identity(dataset_id, credentials.cloud_url)
|
|
1994
|
+
return {
|
|
1995
|
+
"schema_version": "mostlyright-dataset-identity-reconciled.v1",
|
|
1996
|
+
"status": "dataset_identity_synced"
|
|
1997
|
+
if result["public_projection_synced"]
|
|
1998
|
+
else "dataset_identity_sync_pending",
|
|
1999
|
+
"dataset_id": dataset_id,
|
|
2000
|
+
**result,
|
|
2001
|
+
}
|
|
1926
2002
|
if action == "note":
|
|
1927
2003
|
return note(args, client=client, stdin=stdin)
|
|
1928
2004
|
if action == "activity":
|
|
@@ -1930,8 +2006,7 @@ def dataset(
|
|
|
1930
2006
|
if action == "watch":
|
|
1931
2007
|
return watch(args, client=client)
|
|
1932
2008
|
if action == "publish":
|
|
1933
|
-
#
|
|
1934
|
-
# dataset routes; who may read a dataset is Cloud's decision and Cloud's route, so the
|
|
2009
|
+
# This verb and identity sync use Cloud credentials. Access is Cloud's decision, so the
|
|
1935
2010
|
# injected client for this verb is the Cloud one and cannot be a Studio session.
|
|
1936
2011
|
if client is not None and not isinstance(client, CloudDatasetAccessClient):
|
|
1937
2012
|
raise ThinLaneError(
|
|
@@ -1946,6 +2021,11 @@ def dataset(
|
|
|
1946
2021
|
)
|
|
1947
2022
|
|
|
1948
2023
|
|
|
2024
|
+
def dataset_exit_code(payload: dict[str, Any]) -> int:
|
|
2025
|
+
"""Preserve the saved-write receipt but signal an incomplete public sync."""
|
|
2026
|
+
return 2 if payload.get("public_projection_synced") is False else 0
|
|
2027
|
+
|
|
2028
|
+
|
|
1949
2029
|
__all__ = [
|
|
1950
2030
|
"ACCESS_MEMBERS",
|
|
1951
2031
|
"ACCESS_REFUSALS",
|
|
@@ -1973,6 +2053,7 @@ __all__ = [
|
|
|
1973
2053
|
"checked_name",
|
|
1974
2054
|
"create",
|
|
1975
2055
|
"dataset",
|
|
2056
|
+
"dataset_exit_code",
|
|
1976
2057
|
"declare_arguments",
|
|
1977
2058
|
"event_stream",
|
|
1978
2059
|
"note",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/canonical.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/key_seam.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/page_coverage.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/session_probes.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/skill_assets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/table_manifest.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/acquire.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/activity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/approvals.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/categories.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/download.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/narrative.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/parity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/probe.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/propose.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/recipe.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/research.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/stream.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/transport.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/user_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_catalog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_handoff.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_query.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_secrets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_stream.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/v4_tables.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/thin/vocabulary.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/attendance.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/clarification.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/cloud_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/commands/auth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/credentials.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/login.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/path_kind.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/plain_file.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/remediation.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.5}/src/mostlyright/data_harness/ux/render.py
RENAMED
|
File without changes
|