mostlyright-data 0.19.4__tar.gz → 0.19.6__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.6}/PKG-INFO +1 -1
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/pyproject.toml +1 -1
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/skills/mr-data-build/SKILL.md +43 -48
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/router.py +10 -2
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/session.py +3 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_datasets.py +86 -5
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/.gitignore +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/README.md +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/table_manifest.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/recipe.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/attendance.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/remediation.py +0 -0
- {mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/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. |
|
|
@@ -640,16 +640,16 @@ seconds -- not a paragraph, and not a report. Use exactly this shape, in this or
|
|
|
640
640
|
section out only when there is genuinely nothing to say (shown indented here; the document
|
|
641
641
|
itself carries the headings at column one):
|
|
642
642
|
|
|
643
|
-
|
|
644
|
-
|
|
643
|
+
A plain opening paragraph: what the data contains, what question or model it supports, and
|
|
644
|
+
the facts a reader needs before using it.
|
|
645
645
|
|
|
646
646
|
## Coverage
|
|
647
|
-
-
|
|
648
|
-
-
|
|
649
|
-
-
|
|
647
|
+
- Window: the exact dates and the time zone.
|
|
648
|
+
- Grain: one row per what.
|
|
649
|
+
- Cadence: how often rows arrive, and how many per day or month.
|
|
650
650
|
|
|
651
651
|
## Columns
|
|
652
|
-
- `column_name
|
|
652
|
+
- `column_name`: what it measures, in which unit.
|
|
653
653
|
- (one bullet per column; group unit-sharing columns on one line where that reads better)
|
|
654
654
|
|
|
655
655
|
## Missing values
|
|
@@ -665,43 +665,33 @@ That is six sections: the opening paragraph, and the five headings under it.
|
|
|
665
665
|
|
|
666
666
|
**Opening paragraph** (everything before the first `##` heading)
|
|
667
667
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
5.
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
6.
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
Beijing airport weather since 2020, a report about every half hour, with the day's high and
|
|
697
|
-
low on each row. Temperature, wind, pressure and visibility as the airport recorded them.
|
|
698
|
-
|
|
699
|
-
The station code, the report types, the publisher and the count all moved down to Coverage and to
|
|
700
|
-
Source and rights, where a reader who wants them looks. "About every half hour" stays only because
|
|
701
|
-
Coverage puts a month at 1,302 to 1,591 rows, which is one row every 27 to 33 minutes. Three ways
|
|
702
|
-
to fail: the pitch (`Unlock real-time insights into how prediction markets price
|
|
703
|
-
the weather!`), the promise (`ready to test a forecast against what actually happened`) and the
|
|
704
|
-
bare subject with no reason to care (`Beijing weather data.`).
|
|
668
|
+
Write as an engineer explaining a useful table to another engineer. The opening may have more than
|
|
669
|
+
two sentences. Start with the concrete records the dataset contains. Then say which question the
|
|
670
|
+
records help answer or which model they can support. Include geography, time range, row grain,
|
|
671
|
+
update cadence and material limitations when those facts are known. Put a join, transformation or
|
|
672
|
+
source caveat here only when it changes how somebody should use the data.
|
|
673
|
+
|
|
674
|
+
1. The first two sentences must distinguish this dataset from every other dataset in the set. If
|
|
675
|
+
both sentences could describe another dataset unchanged, rewrite them.
|
|
676
|
+
2. Use ordinary verbs such as contains, tracks, joins, updates, records and returns. Vary sentence
|
|
677
|
+
length, and use short sentences for the important facts.
|
|
678
|
+
3. Preserve every established fact. Never invent coverage, freshness, quality, licensing or an
|
|
679
|
+
intended use.
|
|
680
|
+
4. Write `Mostly Right`, with a space, except inside a literal identifier that uses another form.
|
|
681
|
+
5. Use no Markdown in the opening paragraph. Do not use generic openings such as `This dataset
|
|
682
|
+
provides`, marketing claims such as `comprehensive`, `powerful` or `high-quality`, or abstract
|
|
683
|
+
phrases such as `enables insights`, `facilitates analysis` or `serves as a valuable resource`.
|
|
684
|
+
6. Do not explain page design or metadata fields. Do not use em dashes, semicolons, bold labels or
|
|
685
|
+
fake quotations. Explain the data before implementation details.
|
|
686
|
+
|
|
687
|
+
After the build establishes each fact, an airport-weather opening can read:
|
|
688
|
+
|
|
689
|
+
Hourly weather observations from 20 airport stations across the United States. Each row records
|
|
690
|
+
one station report with temperature, dew point, wind, visibility, and observation time. Use it
|
|
691
|
+
to compare observed conditions across stations and time or train a short-term weather model.
|
|
692
|
+
The table keeps multiple reports within an hour when conditions change, so aggregate it before
|
|
693
|
+
using one row per station-hour. Mostly Right refreshes the recent window and preserves older
|
|
694
|
+
observations.
|
|
705
695
|
|
|
706
696
|
**Body rules** (the five sections under the headings)
|
|
707
697
|
1. Every sentence must stay true and complete when quoted alone, away from the page. Never
|
|
@@ -715,21 +705,26 @@ bare subject with no reason to care (`Beijing weather data.`).
|
|
|
715
705
|
5. Everything in the paragraph below still applies.
|
|
716
706
|
|
|
717
707
|
Rules that keep it readable: short sentences; no sentence over about twenty-five words; bullets
|
|
718
|
-
rather than comma lists; no
|
|
708
|
+
rather than comma lists; no bold labels, em dashes, semicolons or fake quotations; no tables, no
|
|
709
|
+
code blocks, no links to internal tools, no headings beyond
|
|
719
710
|
the ones above. Spell out the names a reader would search for -- the place, the identifiers in
|
|
720
711
|
every common form, the programme and the publisher -- once each, under Coverage and under
|
|
721
712
|
Source and rights, and never as a keyword list. A one-line description is a defect to fix in the
|
|
722
713
|
revision, not a style choice. Give `table.description` the grain and the window.
|
|
714
|
+
Every statement in the opening and body must preserve an established fact. Never add unsupported
|
|
715
|
+
coverage, freshness, quality, licensing or intended use.
|
|
723
716
|
|
|
724
717
|
**Check before writing.** Fix and re-check until every line passes:
|
|
725
718
|
- title: at most 60 characters, contains the place, contains no colon, pipe or exclamation mark,
|
|
726
719
|
no brand, no "dataset" or "data", no version number, no run date
|
|
727
|
-
- opening paragraph: two
|
|
728
|
-
|
|
729
|
-
|
|
720
|
+
- opening paragraph: at least two distinct sentences, with the concrete records first and the
|
|
721
|
+
question or model they support next; include known geography, time range, grain, update cadence
|
|
722
|
+
and material limitations; no Markdown, banned style or invented fact
|
|
730
723
|
- category: exactly one fixed ID saved and verified before recipe registration
|
|
731
724
|
- topics: 3 to 8 descriptive tags, no duplicates, lowercase, at most 40 characters each
|
|
732
725
|
- licence: an SPDX identifier the sources actually grant, or `--license` left off
|
|
726
|
+
- whole description: every statement preserves an established fact; no bold labels, em dashes,
|
|
727
|
+
semicolons, fake quotations or unsupported claims
|
|
733
728
|
- every `##` heading in DESCRIPTION.md is one of the five, in that relative order, none repeated
|
|
734
729
|
|
|
735
730
|
**Every dataset gets a cover before its first run is presented.** Once the description is
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/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.6}/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.6}/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.6}/src/mostlyright/data_harness/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/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.6}/src/mostlyright/data_harness/key_seam.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/page_coverage.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/session_probes.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/skill_assets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/table_manifest.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/acquire.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/activity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/approvals.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/categories.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/download.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/narrative.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/parity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/probe.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/propose.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/recipe.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/research.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/stream.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/transport.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/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.6}/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.6}/src/mostlyright/data_harness/thin/v4_handoff.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_query.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_secrets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_stream.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/v4_tables.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/thin/vocabulary.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/attendance.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/clarification.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/cloud_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/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.6}/src/mostlyright/data_harness/ux/credentials.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/login.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/path_kind.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/plain_file.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/remediation.py
RENAMED
|
File without changes
|
{mostlyright_data-0.19.4 → mostlyright_data-0.19.6}/src/mostlyright/data_harness/ux/render.py
RENAMED
|
File without changes
|