mostlyright-data 0.29.3__tar.gz → 0.29.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.29.3 → mostlyright_data-0.29.5}/PKG-INFO +1 -1
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/pyproject.toml +1 -1
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +19 -4
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/making-a-table-incremental.md +67 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/readers.md +46 -1
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/sources.md +24 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/canonical.py +23 -3
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/table_manifest.py +1 -1
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe.py +15 -6
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe_explain.py +58 -3
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/session.py +25 -2
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/studio_capabilities.py +11 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4.py +2 -2
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_datasets.py +32 -55
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_public.py +19 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/credential_store.py +7 -1
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/.gitignore +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/README.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/SKILL.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/agent-protocol.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/boundaries.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/commands.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/installation-parity.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/live-run.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/narrating-the-run.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/one-install.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/prediction-labels.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/preparing-table-search.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/promote.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/receipts.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/reference-pages.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/required-protocol.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/source-credentials.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/transforms.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/user-communication-contract.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/agent_protocol.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/fast_features.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/progress_rate.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/propose.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe_forecast.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/router.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/run_diagnose.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_artifacts.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_connected.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_search.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_tables.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/vocabulary.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/attendance.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/remediation.py +0 -0
- {mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/render.py +0 -0
|
@@ -108,10 +108,25 @@ not an answer yet, so read it back again later. A status of `truncated` is an an
|
|
|
108
108
|
a limit: keep its limits and its evidence with whatever you record, and never call it complete. The
|
|
109
109
|
command takes no key and no Reader options, so a source that needs either, or that is not a CSV, is
|
|
110
110
|
read by the run below instead. So is any source that answers `capability_unavailable`, which means
|
|
111
|
-
this deployment cannot inspect it: do not retry it, and do not guess in its place.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
this deployment cannot inspect it: do not retry it, and do not guess in its place.
|
|
112
|
+
|
|
113
|
+
**Write down what each look found.** An inspection is not a run, and nothing on the page shows what
|
|
114
|
+
it found unless you write it down. Once a look has shown you the start of a file, write one
|
|
115
|
+
`mr-data dataset note DATASET_ID` for that source. The heading names the source and the outcome;
|
|
116
|
+
writing the same heading again revises that cell instead of adding one, so two sources need two
|
|
117
|
+
headings. The body says what the sample showed (the format and the columns) and how much of the file
|
|
118
|
+
it covered (the rows and bytes read, and whether it was cut short). Then count the source in
|
|
119
|
+
`--fact sources_probed` the next time you set the activity line.
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
mr-data dataset note DATASET_ID \
|
|
123
|
+
--heading "Gauge file A is a CSV with four columns, cut short at 1,000 rows" \
|
|
124
|
+
--json <<'EOF'
|
|
125
|
+
The look read the first 1,000 rows (41,208 bytes) of the file and stopped at the row limit, so it
|
|
126
|
+
describes the start of the file and not the whole file. It is a CSV with four columns: station_id,
|
|
127
|
+
observed_at, stage_m and quality_flag.
|
|
128
|
+
EOF
|
|
129
|
+
```
|
|
115
130
|
|
|
116
131
|
**Both obey the ordinary gate.** Registering needs the brief answered or delegated, so the run
|
|
117
132
|
happens AFTER stage 2's answers arrive — or at once under a delegation — and an inspection, which
|
|
@@ -560,3 +560,70 @@ again. Ask the user before passing either flag. Never pass one to clear a refusa
|
|
|
560
560
|
| calls on the first full after the change | every hit whose answer is not held | each hit that passes the filter, once, because that full reads fresh |
|
|
561
561
|
| calls on each weekly rebuild | every hit whose answer is not held | only new hits that pass the filter; the held answers are carried forward |
|
|
562
562
|
| `pages.max_fetches_per_run` | 36,874 | the forecast's suggestion after the first filtered run |
|
|
563
|
+
|
|
564
|
+
#### Keyless API windows (Studio 4.26)
|
|
565
|
+
|
|
566
|
+
Check `studio_capabilities.keyless_window` is literal `true` before using this grammar.
|
|
567
|
+
An absent or false capability means "not available on this Studio yet". Registration and
|
|
568
|
+
file explanation refuse with `THIN_GRAMMAR_NOT_SERVED` before local lint or registration;
|
|
569
|
+
an older API's schema 422 on the explanation also refuses. No dataset delegation is written.
|
|
570
|
+
The capability covers `public.https.api@1.0.0`, `generic_api.auth.kind: none`, endpoint
|
|
571
|
+
`epoch_millis`, the `epoch_millis_value` partition key, and the two request controls below. Existing credential-free file and collection
|
|
572
|
+
adapters do not require this capability.
|
|
573
|
+
|
|
574
|
+
For a public JSON/CSV API use `credential_mode: none`, an HTTPS `origin`, and `generic_api`
|
|
575
|
+
with exact `auth: {"kind":"none"}`. Requests are GET-only, with no body or credential reference.
|
|
576
|
+
Authenticated adapters retain their existing credential requirements. Declare both window
|
|
577
|
+
endpoints with `location: parameter` and corresponding required request parameters. Origin,
|
|
578
|
+
host allowlists and cursor next-URL restrictions still apply.
|
|
579
|
+
|
|
580
|
+
For Binance BTCUSDT one-minute klines, start the backfill at `2017-08-17T00:00:00Z` and request
|
|
581
|
+
`/api/v3/klines` with `symbol=BTCUSDT`, `interval=1m`, `limit=1000`, and parameter templates for
|
|
582
|
+
`startTime` and `endTime`. Use this request window:
|
|
583
|
+
|
|
584
|
+
```json
|
|
585
|
+
{
|
|
586
|
+
"request": {
|
|
587
|
+
"start": {"location": "parameter", "encoding": "epoch_millis", "bound": "inclusive", "parameter": "startTime"},
|
|
588
|
+
"end": {"location": "parameter", "encoding": "epoch_millis", "bound": "inclusive", "parameter": "endTime"},
|
|
589
|
+
"max_slice_seconds": 43200,
|
|
590
|
+
"min_interval_milliseconds": 1000
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Keep `granularity: day`, `timezone: UTC` and `merge.materialization: partition_replace`.
|
|
596
|
+
For the acquired `open_time_ms` column declare
|
|
597
|
+
`merge.partition: {"column":"open_time_ms","key":"epoch_millis_value"}` and
|
|
598
|
+
`merge.row_identity: ["open_time_ms"]`. This encoding maps the integer millisecond cell to
|
|
599
|
+
its UTC calendar day before transforms; it floors negative epochs correctly too.
|
|
600
|
+
The 43,200-second maximum cuts each UTC day into two 12-hour requests, each with at most 720
|
|
601
|
+
one-minute bars under the publisher's 1,000-row cap. Slices are requests, never new partitions.
|
|
602
|
+
An inclusive millisecond end renders the next slice boundary minus 1 ms; adjacent slices do not
|
|
603
|
+
overlap. An exclusive start renders the start minus 1 ms. The older date/epoch-second encodings
|
|
604
|
+
retain their day-step conversions. Slice bounds require both endpoints to use milliseconds.
|
|
605
|
+
`max_slice_seconds` is 1–86,400; `window.request.min_interval_milliseconds` is 1–3,600,000 and is supported on
|
|
606
|
+
the new public generic adapter, defaulting to 1,000 when omitted. Pacing applies across the
|
|
607
|
+
entire walk, including pagination, rather than resetting at each day.
|
|
608
|
+
|
|
609
|
+
After the full backfill, choose an hourly cadence in the agreed brief. Refresh re-reads the
|
|
610
|
+
current UTC day, flooring a catch-up start to midnight when prior coverage reached the next
|
|
611
|
+
midnight; it replaces that day's partition. Do not set `complete_period_only` when the open day
|
|
612
|
+
is needed. Size `limits.max_requests` for every slice and every pagination page across the full
|
|
613
|
+
walk; the global budget must allow it to finish. The 4,096-partition ceiling still counts days.
|
|
614
|
+
Python and Keel plan the same windows; large declared relations use the admitted Keel route,
|
|
615
|
+
with each day still subject to its in-memory ceiling. A declaration is not evidence of a
|
|
616
|
+
completed large ingestion: verify actual coverage from the run.
|
|
617
|
+
|
|
618
|
+
The [Reader example](readers.md#needs-studio-426) maps Binance's root array of 12-column arrays.
|
|
619
|
+
An explicit `table.partition` may use `{"column":"open_time_ms","encoding":"epoch_millis_value"}`;
|
|
620
|
+
that table encoding also requires `keyless_window`. The native refresh subset preserves this
|
|
621
|
+
partition column with exact `CAST(open_time_ms AS BIGINT) AS open_time_ms`; its current subset
|
|
622
|
+
requires `checks: []`. Verify the built values and coverage with bounded queries and receipts.
|
|
623
|
+
Bind the real dataset ID before Harness registration, following the brief workflow; the fixture
|
|
624
|
+
omits an ID rather than inventing an existing dataset.
|
|
625
|
+
The raw millisecond timestamp uses `epoch_millis_value`, never `iso_date_prefix`;
|
|
626
|
+
a transform does not run before source partition merging. Existing JSON cursor pagination is reused unchanged. XML readers
|
|
627
|
+
do not add extraction of arXiv OAI-PMH XML continuation tokens to JSON-pointer pagination.
|
|
628
|
+
Wikipedia's existing `public.https` date-in-path source remains a literal full fetch; this
|
|
629
|
+
example does not prove an entire historical date-path backfill.
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/readers.md
RENAMED
|
@@ -119,7 +119,7 @@ decoder. Zero matches and multiple matches both refuse.
|
|
|
119
119
|
| `raster.geospatial@1.0.0` | `format`, exact native `crs`, and an `x`/`y`/`width`/`height` window. |
|
|
120
120
|
| `satellite.sentinel_safe@1.0.0` | SAFE ZIP with exact spectral/quality member names, native CRS, quality layer, and an `x`/`y`/`width`/`height` window. |
|
|
121
121
|
| `image.rendered@1.0.0` | `format` plus a bounded `x`/`y`/`width`/`height` rendered-image crop. |
|
|
122
|
-
| `scientific.fits@1.0.0` | `
|
|
122
|
+
| `scientific.fits@1.0.0` | `band_id: "195"` plus an exact native `x`/`y`/`width`/`height` image window. |
|
|
123
123
|
| `satellite.himawari_hsd@1.0.0` | Certified HSD v1.3 single-segment B13 plus an exact native `x`/`y`/`width`/`height` radiance window. |
|
|
124
124
|
| `satellite.himawari_hsd_complete@1.0.0` | Complete HSD v1.3 scan ZIP: `band_id`, exact ordered `segment_members`, and a native `x`/`y`/`width`/`height` radiance window. |
|
|
125
125
|
| `satellite.xrit_image@1.0.0` | Exact ZIP prologue, eight ordered HRIT/LRIT image segments and epilogue; sealed channel/grid/navigation plus a bounded raw-count window. |
|
|
@@ -140,3 +140,48 @@ settings. Spreadsheet receipts also identify stored calculated values.
|
|
|
140
140
|
Pinned HTTPS Reader sources retrieve, decode, and parse in one attested Clean room call. JSON,
|
|
141
141
|
NDJSON, ZIP, TAR, GZIP, XLSX, delimited text, and single-record GRIB2 are supported when the
|
|
142
142
|
response matches the family's exact media-type, settings, and budget contract.
|
|
143
|
+
|
|
144
|
+
### Needs Studio 4.26
|
|
145
|
+
|
|
146
|
+
Check first: run `mr-data recipe explain RECIPE.json --json` and read `studio_capabilities`.
|
|
147
|
+
Use `public.https.api@1.0.0` only when `keyless_window` is literal `true`; otherwise report
|
|
148
|
+
"not available on this Studio yet". Registration and file explanation refuse with
|
|
149
|
+
`THIN_GRAMMAR_NOT_SERVED`, including an older API's schema 422 before capabilities.
|
|
150
|
+
|
|
151
|
+
The new adapter uses the existing `generic_api.response` Reader contract. For Binance klines,
|
|
152
|
+
state `format: json`, `records_pointer: ""`, and `reader.family_id: json.tabular` with
|
|
153
|
+
`family_version: 1.0.0` (or the already admitted `1.1.0`). Pin `options_digest` to the SHA-256
|
|
154
|
+
of the canonical decode options, rather than inserting a placeholder digest. These decode
|
|
155
|
+
options project a root JSON array of arrays, with RFC 6901 numeric indexes into each row:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"document_format": "json",
|
|
160
|
+
"records_pointer": "",
|
|
161
|
+
"expand": [],
|
|
162
|
+
"columns": [
|
|
163
|
+
{"name": "open_time_ms", "pointer": "/0"},
|
|
164
|
+
{"name": "open", "pointer": "/1"},
|
|
165
|
+
{"name": "high", "pointer": "/2"},
|
|
166
|
+
{"name": "low", "pointer": "/3"},
|
|
167
|
+
{"name": "close", "pointer": "/4"},
|
|
168
|
+
{"name": "volume", "pointer": "/5"},
|
|
169
|
+
{"name": "close_time_ms", "pointer": "/6"},
|
|
170
|
+
{"name": "quote_volume", "pointer": "/7"},
|
|
171
|
+
{"name": "trades", "pointer": "/8"},
|
|
172
|
+
{"name": "taker_buy_base_volume", "pointer": "/9"},
|
|
173
|
+
{"name": "taker_buy_quote_volume", "pointer": "/10"},
|
|
174
|
+
{"name": "unused", "pointer": "/11"}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Every column is required by default. The Reader projects values; it does not turn an epoch
|
|
180
|
+
millisecond cell into an ISO date. Declare numeric/decimal types in the table transform and
|
|
181
|
+
declare `window.merge.partition: {"column":"open_time_ms","key":"epoch_millis_value"}`.
|
|
182
|
+
The key maps the acquired integer millisecond value to its UTC day before transforms;
|
|
183
|
+
`merge.row_identity` is `["open_time_ms"]`. It also needs `keyless_window`. See
|
|
184
|
+
[the cap-safe window](making-a-table-incremental.md#keyless-api-windows-studio-426) for the
|
|
185
|
+
backfill, hourly cadence, shared Python/Keel windows and request limits. Cursor pagination
|
|
186
|
+
continues to use its existing JSON next-token pointer; XML OAI-PMH token parsing is not supplied
|
|
187
|
+
by this mapping.
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/sources.md
RENAMED
|
@@ -271,3 +271,27 @@ its upstream cannot refresh incrementally. A `closed` or `reference` source may
|
|
|
271
271
|
table, and a refresh restores it from the predecessor (ADR 0061). Do not split a table
|
|
272
272
|
to separate them. No per-row collection may derive its rows from a `closed` or `reference` source.
|
|
273
273
|
[Which plans refresh](making-a-table-incremental.md#which-plans-refresh) has the rest.
|
|
274
|
+
|
|
275
|
+
### Needs Studio 4.26
|
|
276
|
+
|
|
277
|
+
Check first: run `mr-data recipe explain RECIPE.json --json` and read `studio_capabilities`.
|
|
278
|
+
Use the following only when `keyless_window` is literal `true`; otherwise report "not available
|
|
279
|
+
on this Studio yet". The client refuses with `THIN_GRAMMAR_NOT_SERVED` before registration,
|
|
280
|
+
including when an older API rejects the explanation document with schema 422.
|
|
281
|
+
|
|
282
|
+
`public.https.api@1.0.0` is the credential-free generic HTTPS API adapter. State
|
|
283
|
+
`credential_mode: none`, `origin`, and `generic_api` with exact `auth: {"kind":"none"}`.
|
|
284
|
+
Use GET only: no request body, credential reference, token, secret header or secret query value.
|
|
285
|
+
This adapter does not relax authentication on the keyed adapters. The owned HTTPS origin,
|
|
286
|
+
host allowlist, bounded request budgets and cursor next-URL restrictions remain in force.
|
|
287
|
+
The existing generic response mapping and JSON/CSV pagination apply; a cursor needs the
|
|
288
|
+
publisher's actual next-token pointer. JSON-pointer pagination does not extract XML OAI-PMH tokens.
|
|
289
|
+
|
|
290
|
+
For Binance klines use parameter-windowed `startTime`/`endTime`, millisecond endpoints and
|
|
291
|
+
12-hour slices at one request per second. Follow the backfill and hourly-refresh example in
|
|
292
|
+
[making a table incremental](making-a-table-incremental.md#keyless-api-windows-studio-426)
|
|
293
|
+
and the root-array projection in [Readers](readers.md#needs-studio-426). Keep the day partition
|
|
294
|
+
with `epoch_millis_value` on the raw millisecond time column, and size the request ceiling
|
|
295
|
+
for all slices and pages. Do not substitute a whole-day query:
|
|
296
|
+
1,440 one-minute bars exceed Binance's 1,000-row response cap. Existing credential-free
|
|
297
|
+
`public.https` file fetches and `public.https.collection@2.0.0` keep their own contracts.
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/canonical.py
RENAMED
|
@@ -14,6 +14,7 @@ from typing import Any, NoReturn
|
|
|
14
14
|
|
|
15
15
|
MAX_CANONICAL_DEPTH = 64
|
|
16
16
|
MAX_CANONICAL_MEMBERS = 100_000
|
|
17
|
+
_MAX_INTEGER_LITERAL_LENGTH = len(str(-(2**63)))
|
|
17
18
|
MAX_CANONICAL_STRING_LENGTH = 1_000_000
|
|
18
19
|
MAX_CANONICAL_INTEGER = (1 << 63) - 1
|
|
19
20
|
|
|
@@ -35,7 +36,10 @@ def parse_json(value: str | bytes | bytearray | memoryview) -> Any:
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def parse_foreign_json(
|
|
38
|
-
value: str | bytes | bytearray | memoryview,
|
|
39
|
+
value: str | bytes | bytearray | memoryview,
|
|
40
|
+
*,
|
|
41
|
+
max_members: int = MAX_CANONICAL_MEMBERS,
|
|
42
|
+
wide_integers: bool = False,
|
|
39
43
|
) -> Any:
|
|
40
44
|
"""Parse a third-party document with the same hardening, but accept fractional numbers.
|
|
41
45
|
|
|
@@ -59,9 +63,14 @@ def parse_foreign_json(
|
|
|
59
63
|
|
|
60
64
|
``max_members`` raises the member ceiling for one document whose size a route already bounds,
|
|
61
65
|
such as a table manifest listing thousands of parts.
|
|
66
|
+
|
|
67
|
+
``wide_integers`` reads an integer literal outside the signed 64-bit range as a finite float
|
|
68
|
+
rather than refusing the document. It is for an answer that carries a table's own values from
|
|
69
|
+
a publisher that writes a whole-valued double without a fraction or an exponent, so ``1e20``
|
|
70
|
+
arrives as ``100000000000000000000``. An integer inside the range is still an ``int``.
|
|
62
71
|
"""
|
|
63
72
|
|
|
64
|
-
return _parse(value, fractions="accept", max_members=max_members)
|
|
73
|
+
return _parse(value, fractions="accept", max_members=max_members, wide_integers=wide_integers)
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
def _parse(
|
|
@@ -69,6 +78,7 @@ def _parse(
|
|
|
69
78
|
*,
|
|
70
79
|
fractions: str,
|
|
71
80
|
max_members: int = MAX_CANONICAL_MEMBERS,
|
|
81
|
+
wide_integers: bool = False,
|
|
72
82
|
) -> Any:
|
|
73
83
|
if isinstance(value, str):
|
|
74
84
|
text = value
|
|
@@ -98,7 +108,7 @@ def _parse(
|
|
|
98
108
|
parsed = json.loads(
|
|
99
109
|
text,
|
|
100
110
|
object_pairs_hook=_reject_duplicate_pairs,
|
|
101
|
-
parse_int=_parse_integer,
|
|
111
|
+
parse_int=_parse_wide_integer if wide_integers else _parse_integer,
|
|
102
112
|
parse_float=_parse_fraction if accept_fractions else _reject_fraction,
|
|
103
113
|
parse_constant=_reject_constant,
|
|
104
114
|
)
|
|
@@ -318,6 +328,16 @@ def _parse_integer(value: str) -> int:
|
|
|
318
328
|
return parsed
|
|
319
329
|
|
|
320
330
|
|
|
331
|
+
def _parse_wide_integer(value: str) -> int | float:
|
|
332
|
+
"""Read an integer literal, as a float when it is outside the signed 64-bit range."""
|
|
333
|
+
|
|
334
|
+
if len(value) <= _MAX_INTEGER_LITERAL_LENGTH:
|
|
335
|
+
parsed = int(value)
|
|
336
|
+
if -MAX_CANONICAL_INTEGER - 1 <= parsed <= MAX_CANONICAL_INTEGER:
|
|
337
|
+
return parsed
|
|
338
|
+
return _parse_fraction(value)
|
|
339
|
+
|
|
340
|
+
|
|
321
341
|
def _parse_fraction(value: str) -> float:
|
|
322
342
|
"""Read a fractional literal from a foreign document, refusing a non-finite one."""
|
|
323
343
|
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/table_manifest.py
RENAMED
|
@@ -101,7 +101,7 @@ _SUPPORTED_MANIFEST_SCHEMAS = frozenset((TABLE_MANIFEST_SCHEMA, TABLE_MANIFEST_S
|
|
|
101
101
|
#: ``iso_month_prefix`` tables read as TABLE_MANIFEST_INVALID on 0.28.0 (planning#807).
|
|
102
102
|
#: ``tests/test_thin_table_manifest_encodings.py`` holds this equal to the vendored recipe schema.
|
|
103
103
|
MANIFEST_PARTITION_ENCODINGS: frozenset[str] = frozenset(
|
|
104
|
-
{"value", "iso_date_prefix", "iso_date_value", "iso_month_prefix"}
|
|
104
|
+
{"value", "iso_date_prefix", "iso_date_value", "iso_month_prefix", "epoch_millis_value"}
|
|
105
105
|
)
|
|
106
106
|
|
|
107
107
|
#: The two media types the format adds to the closed artifact vocabulary.
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe.py
RENAMED
|
@@ -1181,13 +1181,21 @@ def register(
|
|
|
1181
1181
|
) -> dict[str, Any]:
|
|
1182
1182
|
"""``mr-data recipe FILE``: register one document and print what the server derived from it.
|
|
1183
1183
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
before
|
|
1187
|
-
whether this registration should happen at all. The registration itself is last.
|
|
1184
|
+
Existing documents are linted before a credential is resolved. A keyless-window document
|
|
1185
|
+
asks what Studio serves before local lint, because an older schema may reject the grammar
|
|
1186
|
+
before reporting capabilities. The brief and delegation gates follow; registration is last.
|
|
1188
1187
|
"""
|
|
1189
1188
|
|
|
1190
1189
|
document = read_document(Path(args.target))
|
|
1190
|
+
# New grammar must be refused as unserved before local lint can diagnose its shape.
|
|
1191
|
+
keyless = any(
|
|
1192
|
+
capability == "keyless_window" for _, capability in recipe_explain.gated_grammar(document)
|
|
1193
|
+
)
|
|
1194
|
+
selected = client
|
|
1195
|
+
served = []
|
|
1196
|
+
if keyless:
|
|
1197
|
+
selected = selected or _client(args)
|
|
1198
|
+
served = recipe_explain.registration_gate(selected, document)
|
|
1191
1199
|
check_document(document)
|
|
1192
1200
|
if not getattr(args, "no_lint", False):
|
|
1193
1201
|
refusal = lint_refusal(
|
|
@@ -1239,13 +1247,14 @@ def register(
|
|
|
1239
1247
|
)
|
|
1240
1248
|
delegated = getattr(args, "delegated", None)
|
|
1241
1249
|
blocks = delegation_blocks(delegated) if delegated is not None else None
|
|
1242
|
-
selected =
|
|
1250
|
+
selected = selected or _client(args)
|
|
1243
1251
|
# ⚠ THE CAPABILITY GATE COMES BEFORE ANYTHING IS WRITTEN. A document using grammar this
|
|
1244
1252
|
# Studio does not serve (`row_filter`, a per-row `interval` revisit) is refused here, before
|
|
1245
1253
|
# the delegation append below and before the registration, so a refusal leaves nothing
|
|
1246
1254
|
# behind. It asks Studio only when the document uses such grammar or a snapshot window, so
|
|
1247
1255
|
# every other registration makes exactly the calls it made before.
|
|
1248
|
-
|
|
1256
|
+
if not keyless:
|
|
1257
|
+
served = recipe_explain.registration_gate(selected, document)
|
|
1249
1258
|
warnings += served
|
|
1250
1259
|
warnings += sampled
|
|
1251
1260
|
warning_codes = [recipe_explain.SNAPSHOT_WILL_NOT_REFRESH_YET] if served else []
|
|
@@ -125,6 +125,7 @@ CAPABILITY_NAMES: tuple[str, ...] = (
|
|
|
125
125
|
"fresh_read_control",
|
|
126
126
|
"snapshot_refresh",
|
|
127
127
|
"row_request_batch",
|
|
128
|
+
"keyless_window",
|
|
128
129
|
)
|
|
129
130
|
|
|
130
131
|
#: What a Studio that answers 404 or 405 from the explanation routes serves (README D27).
|
|
@@ -1246,6 +1247,17 @@ def explain_document_at_studio(
|
|
|
1246
1247
|
except ThinLaneError as refusal:
|
|
1247
1248
|
if route_unavailable(refusal):
|
|
1248
1249
|
return None
|
|
1250
|
+
keyless = [pointer for pointer, capability in gated_grammar(document)
|
|
1251
|
+
if capability == "keyless_window"]
|
|
1252
|
+
if keyless and getattr(refusal, "http_status", None) == 422:
|
|
1253
|
+
# Older API schemas reject the document before they can answer capabilities.
|
|
1254
|
+
# A schema rejection is never evidence that this grammar is served.
|
|
1255
|
+
raise ThinLaneError(
|
|
1256
|
+
GRAMMAR_NOT_SERVED_CODE,
|
|
1257
|
+
f"not available on this Studio yet: {', '.join(keyless)} "
|
|
1258
|
+
"(Studio rejected the explanation document before reporting capabilities). "
|
|
1259
|
+
"Nothing was registered. Wait for the Studio deploy, or remove the member",
|
|
1260
|
+
) from refusal
|
|
1249
1261
|
raise
|
|
1250
1262
|
return _explanation(answer, EXPLAIN_REFRESH_PATH)
|
|
1251
1263
|
|
|
@@ -1286,14 +1298,42 @@ def gated_grammar(document: Mapping[str, Any]) -> list[tuple[str, str]]:
|
|
|
1286
1298
|
|
|
1287
1299
|
``row_filter`` on any collection discovery, and a per-row request collection whose
|
|
1288
1300
|
``pages.revisit`` states ``interval`` (4.14.0). A revisit on any other collection is old
|
|
1289
|
-
grammar. ``batch`` on a per-row request (4.15.0).
|
|
1301
|
+
grammar. ``batch`` on a per-row request (4.15.0). The public generic API, no-auth kind,
|
|
1302
|
+
millisecond endpoints and slice/pacing members (4.26.0) each need ``keyless_window``.
|
|
1290
1303
|
"""
|
|
1291
1304
|
|
|
1292
1305
|
gated: list[tuple[str, str]] = []
|
|
1306
|
+
table = document.get("table")
|
|
1307
|
+
partition = table.get("partition") if isinstance(table, Mapping) else None
|
|
1308
|
+
if isinstance(partition, Mapping) and partition.get("encoding") == "epoch_millis_value":
|
|
1309
|
+
gated.append(("/table/partition/encoding", "keyless_window"))
|
|
1293
1310
|
sources = document.get("sources")
|
|
1294
1311
|
for index, source in enumerate(sources if isinstance(sources, list) else ()):
|
|
1295
1312
|
if not isinstance(source, Mapping):
|
|
1296
1313
|
continue
|
|
1314
|
+
prefix = f"/sources/{index}"
|
|
1315
|
+
connector = source.get("connector")
|
|
1316
|
+
if isinstance(connector, Mapping):
|
|
1317
|
+
if connector.get("adapter_id") == "public.https.api@1.0.0":
|
|
1318
|
+
gated.append((f"{prefix}/connector/adapter_id", "keyless_window"))
|
|
1319
|
+
generic = connector.get("generic_api")
|
|
1320
|
+
auth = generic.get("auth") if isinstance(generic, Mapping) else None
|
|
1321
|
+
if isinstance(auth, Mapping) and auth.get("kind") == "none":
|
|
1322
|
+
gated.append((f"{prefix}/connector/generic_api/auth/kind", "keyless_window"))
|
|
1323
|
+
window = source.get("window")
|
|
1324
|
+
merge = window.get("merge") if isinstance(window, Mapping) else None
|
|
1325
|
+
partition = merge.get("partition") if isinstance(merge, Mapping) else None
|
|
1326
|
+
if isinstance(partition, Mapping) and partition.get("key") == "epoch_millis_value":
|
|
1327
|
+
gated.append((f"{prefix}/window/merge/partition/key", "keyless_window"))
|
|
1328
|
+
window_request = window.get("request") if isinstance(window, Mapping) else None
|
|
1329
|
+
if isinstance(window_request, Mapping):
|
|
1330
|
+
for endpoint in ("start", "end"):
|
|
1331
|
+
value = window_request.get(endpoint)
|
|
1332
|
+
if isinstance(value, Mapping) and value.get("encoding") == "epoch_millis":
|
|
1333
|
+
gated.append((f"{prefix}/window/request/{endpoint}/encoding", "keyless_window"))
|
|
1334
|
+
for member in ("max_slice_seconds", "min_interval_milliseconds"):
|
|
1335
|
+
if member in window_request:
|
|
1336
|
+
gated.append((f"{prefix}/window/request/{member}", "keyless_window"))
|
|
1297
1337
|
collection = source.get("collection")
|
|
1298
1338
|
if not isinstance(collection, Mapping):
|
|
1299
1339
|
continue
|
|
@@ -1348,6 +1388,16 @@ def registration_gate(client: StudioV4Client, document: Mapping[str, Any]) -> li
|
|
|
1348
1388
|
if not gated and not snapshots:
|
|
1349
1389
|
return []
|
|
1350
1390
|
answer = explain_document_at_studio(client, document)
|
|
1391
|
+
return grammar_gate(document, answer)
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
def grammar_gate(
|
|
1395
|
+
document: Mapping[str, Any], answer: Mapping[str, Any] | None
|
|
1396
|
+
) -> list[str]:
|
|
1397
|
+
"""Admit only grammar explicitly served by an explanation; perform no writes."""
|
|
1398
|
+
|
|
1399
|
+
gated = gated_grammar(document)
|
|
1400
|
+
snapshots = snapshot_sources(document)
|
|
1351
1401
|
capabilities = served_capabilities(answer)
|
|
1352
1402
|
missing = [pointer for pointer, capability in gated if not capabilities[capability]]
|
|
1353
1403
|
if missing:
|
|
@@ -1455,10 +1505,15 @@ def explain(args: argparse.Namespace, *, client: StudioV4Client | None = None) -
|
|
|
1455
1505
|
answer = explain_table_at_studio(selected, registered["table_id"])
|
|
1456
1506
|
else:
|
|
1457
1507
|
document = recipe_module.read_document(Path(target))
|
|
1508
|
+
selected = client or recipe_module._client(args)
|
|
1509
|
+
keyless = any(capability == "keyless_window" for _, capability in gated_grammar(document))
|
|
1510
|
+
if keyless:
|
|
1511
|
+
answer = explain_document_at_studio(selected, document)
|
|
1512
|
+
grammar_gate(document, answer)
|
|
1458
1513
|
recipe_module.check_document(document)
|
|
1459
1514
|
subject = {"kind": "document", "table_id": None, "recipe_id": None, "recipe_digest": None}
|
|
1460
|
-
|
|
1461
|
-
|
|
1515
|
+
if not keyless:
|
|
1516
|
+
answer = explain_document_at_studio(selected, document)
|
|
1462
1517
|
capabilities = served_capabilities(answer)
|
|
1463
1518
|
# The mirror is judged under the fence Studio states, so the two can only disagree about
|
|
1464
1519
|
# the document; with no Studio to ask the fence reads closed, as a pre-4.14.0 Studio runs.
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/session.py
RENAMED
|
@@ -26,7 +26,12 @@ from typing import Any
|
|
|
26
26
|
from urllib.parse import quote, urlencode
|
|
27
27
|
from uuid import UUID
|
|
28
28
|
|
|
29
|
-
from mostlyright.data_harness.canonical import
|
|
29
|
+
from mostlyright.data_harness.canonical import (
|
|
30
|
+
MAX_CANONICAL_MEMBERS,
|
|
31
|
+
CanonicalJSONError,
|
|
32
|
+
parse_foreign_json,
|
|
33
|
+
parse_json,
|
|
34
|
+
)
|
|
30
35
|
from mostlyright.data_harness.thin.transport import (
|
|
31
36
|
MAX_JSON_RESPONSE_BYTES,
|
|
32
37
|
ThinLaneError,
|
|
@@ -70,6 +75,9 @@ CLOUD_DATASETS_LIST_PATH = "/api/cli/datasets"
|
|
|
70
75
|
# Projection repair after a Studio metadata write, without a publication mutation.
|
|
71
76
|
CLOUD_DATASET_IDENTITY_PATH = "/api/cli/datasets/{dataset_id}/identity"
|
|
72
77
|
|
|
78
|
+
# Shared UI/CLI archive orchestration, including public projection withdrawal.
|
|
79
|
+
CLOUD_DATASET_ARCHIVE_PATH = "/api/cli/datasets/{dataset_id}/archive"
|
|
80
|
+
|
|
73
81
|
#: The one Cloud address a Dataset has. ``mostlyright-cloud`` serves it at this route with
|
|
74
82
|
#: section suffixes, and a run has no page of its own: its evidence lives on this page's Harness
|
|
75
83
|
#: card, so a receipt names its run with :func:`run_dashboard_url` as a query member here rather
|
|
@@ -620,6 +628,8 @@ class CloudClient:
|
|
|
620
628
|
expected: tuple[int, ...] = (200,),
|
|
621
629
|
absent: str | None = None,
|
|
622
630
|
maximum: int = MAX_TOKEN_RESPONSE_BYTES,
|
|
631
|
+
table_values: bool = False,
|
|
632
|
+
max_members: int = MAX_CANONICAL_MEMBERS,
|
|
623
633
|
) -> dict[str, Any]:
|
|
624
634
|
"""Make one Cloud call and return the object it answered with, or raise the refusal.
|
|
625
635
|
|
|
@@ -633,6 +643,14 @@ class CloudClient:
|
|
|
633
643
|
|
|
634
644
|
``absent`` is what this route's own 404 means, in the caller's words; see
|
|
635
645
|
:func:`cloud_refusal` for why one sentence could not serve both routes.
|
|
646
|
+
|
|
647
|
+
``table_values`` is for a route whose answer carries a table's own values. It admits a
|
|
648
|
+
finite fractional number, because a DOUBLE column or an average is not an integer, and it
|
|
649
|
+
reads an integer literal outside the signed 64-bit range as a float, because Cloud writes
|
|
650
|
+
a whole-valued double without a fraction. ``max_members`` is that answer's member ceiling,
|
|
651
|
+
for a route that states how large a page it may be handed. Every other hardening is
|
|
652
|
+
unchanged, and the default refuses all of it as it does for every answer this lane reads
|
|
653
|
+
as a document of its own.
|
|
636
654
|
"""
|
|
637
655
|
|
|
638
656
|
headers = {
|
|
@@ -651,7 +669,11 @@ class CloudClient:
|
|
|
651
669
|
maximum,
|
|
652
670
|
)
|
|
653
671
|
try:
|
|
654
|
-
parsed =
|
|
672
|
+
parsed = (
|
|
673
|
+
parse_foreign_json(raw, max_members=max_members, wide_integers=True)
|
|
674
|
+
if table_values
|
|
675
|
+
else parse_json(raw)
|
|
676
|
+
)
|
|
655
677
|
except CanonicalJSONError:
|
|
656
678
|
parsed = None
|
|
657
679
|
if status not in expected:
|
|
@@ -668,6 +690,7 @@ __all__ = [
|
|
|
668
690
|
"CLOUD_APPROVAL_DASHBOARD_PATH",
|
|
669
691
|
"CLOUD_DATASETS_LIST_PATH",
|
|
670
692
|
"CLOUD_DATASET_ACCESS_PATH",
|
|
693
|
+
"CLOUD_DATASET_ARCHIVE_PATH",
|
|
671
694
|
"CLOUD_DATASET_DASHBOARD_PATH",
|
|
672
695
|
"CLOUD_DATASET_RESOLVED_SCHEMA",
|
|
673
696
|
"CLOUD_DATASET_RESOLVE_PATH",
|
|
@@ -36,12 +36,14 @@ FRESH_READ_CONTROL = "fresh_read_control"
|
|
|
36
36
|
SNAPSHOT_REFRESH = "snapshot_refresh"
|
|
37
37
|
#: Served by every 4.15.0 API, which admits ``batch`` on a per-row request. An older API omits it.
|
|
38
38
|
ROW_REQUEST_BATCH = "row_request_batch"
|
|
39
|
+
KEYLESS_WINDOW = "keyless_window"
|
|
39
40
|
CAPABILITY_NAMES: tuple[str, ...] = (
|
|
40
41
|
ROW_FILTER,
|
|
41
42
|
PER_ROW_REVISIT_INTERVAL,
|
|
42
43
|
FRESH_READ_CONTROL,
|
|
43
44
|
SNAPSHOT_REFRESH,
|
|
44
45
|
ROW_REQUEST_BATCH,
|
|
46
|
+
KEYLESS_WINDOW,
|
|
45
47
|
)
|
|
46
48
|
|
|
47
49
|
#: What a Studio that serves neither explanation route serves.
|
|
@@ -96,6 +98,14 @@ def document_capabilities(client: StudioV4Client, document: Mapping[str, Any]) -
|
|
|
96
98
|
read. Studio judges the document alone and writes nothing.
|
|
97
99
|
"""
|
|
98
100
|
|
|
101
|
+
from mostlyright.data_harness.thin.recipe_explain import (
|
|
102
|
+
explain_document_at_studio,
|
|
103
|
+
gated_grammar,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if any(capability == KEYLESS_WINDOW for _, capability in gated_grammar(document)):
|
|
107
|
+
# Share the older-schema 422 translation with registration and file explanation.
|
|
108
|
+
return served_capabilities(explain_document_at_studio(client, document))
|
|
99
109
|
try:
|
|
100
110
|
answer = client.explain_recipe_refresh(document)
|
|
101
111
|
except ThinLaneError as refusal:
|
|
@@ -109,6 +119,7 @@ __all__ = [
|
|
|
109
119
|
"CAPABILITY_NAMES",
|
|
110
120
|
"FRESH_READ_CONTROL",
|
|
111
121
|
"GRAMMAR_NOT_SERVED_CODE",
|
|
122
|
+
"KEYLESS_WINDOW",
|
|
112
123
|
"NO_CAPABILITIES",
|
|
113
124
|
"PER_ROW_REVISIT_INTERVAL",
|
|
114
125
|
"ROW_FILTER",
|
|
@@ -147,8 +147,8 @@ from mostlyright.data_harness.thin.transport import ThinLaneError
|
|
|
147
147
|
#: ``JOB_INVALID`` outright, which in production was every refresh a collection epoch was offered
|
|
148
148
|
#: for rather than only the collection runs. This package must not reach a Studio older than the
|
|
149
149
|
#: commit it pins; ``docs/V4-WORKER-PROTOCOL.md`` states it beside the layout's own ordering rule.
|
|
150
|
-
PINNED_V4_OPENAPI_SOURCE_SHA256 = "
|
|
151
|
-
PINNED_V4_CONTRACT_VERSION = "4.
|
|
150
|
+
PINNED_V4_OPENAPI_SOURCE_SHA256 = "1f30e109b81f6aa55c00c8999fd877b6678b7e0bea617707d26e7a040380f98b"
|
|
151
|
+
PINNED_V4_CONTRACT_VERSION = "4.27.0"
|
|
152
152
|
|
|
153
153
|
#: The ``schema_version`` every command body that predates 4.14.0 stamps: ``dataset activity``,
|
|
154
154
|
#: ``table rename``, ``table archive`` and ``dataset archive``. It is the number the 0.25 line
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_datasets.py
RENAMED
|
@@ -127,6 +127,7 @@ from mostlyright.data_harness.thin.narrative import (
|
|
|
127
127
|
from mostlyright.data_harness.thin.runs import new_idempotency_key
|
|
128
128
|
from mostlyright.data_harness.thin.session import (
|
|
129
129
|
CLOUD_DATASET_ACCESS_PATH,
|
|
130
|
+
CLOUD_DATASET_ARCHIVE_PATH,
|
|
130
131
|
CLOUD_DATASET_IDENTITY_PATH,
|
|
131
132
|
CLOUD_DATASET_RESOLVE_PATH,
|
|
132
133
|
CLOUD_DATASET_RESOLVED_SCHEMA,
|
|
@@ -149,7 +150,6 @@ from mostlyright.data_harness.thin.stream import RunEventStream
|
|
|
149
150
|
from mostlyright.data_harness.thin.transport import ThinLaneError, Transport, UrlLibTransport
|
|
150
151
|
from mostlyright.data_harness.thin.v4 import (
|
|
151
152
|
ARCHIVE_DATASET_PATH,
|
|
152
|
-
COMMAND_REQUEST_VERSION_FLOOR,
|
|
153
153
|
CREATE_DATASET_PATH,
|
|
154
154
|
DATASET_CURRENT_STATE_PATH,
|
|
155
155
|
DATASET_EVENT_HISTORY_PATH,
|
|
@@ -448,6 +448,14 @@ class CloudDatasetAccessClient(CloudClient):
|
|
|
448
448
|
credential header and the refusal folding to drift.
|
|
449
449
|
"""
|
|
450
450
|
|
|
451
|
+
def archive_dataset(self, dataset_id: str, confirmation_name: str) -> dict[str, Any]:
|
|
452
|
+
return self.call(
|
|
453
|
+
"POST",
|
|
454
|
+
CLOUD_DATASET_ARCHIVE_PATH.format(dataset_id=dataset_id),
|
|
455
|
+
act="archive this dataset and withdraw its public listings",
|
|
456
|
+
body={"confirmation_name": confirmation_name},
|
|
457
|
+
)
|
|
458
|
+
|
|
451
459
|
def sync_dataset_identity(self, dataset_id: str) -> dict[str, Any]:
|
|
452
460
|
"""Carry Studio's current metadata into Cloud's existing public projection."""
|
|
453
461
|
|
|
@@ -458,7 +466,6 @@ class CloudDatasetAccessClient(CloudClient):
|
|
|
458
466
|
body={},
|
|
459
467
|
)
|
|
460
468
|
|
|
461
|
-
|
|
462
469
|
def list_datasets(self, *, limit: int | None = None) -> dict[str, Any]:
|
|
463
470
|
"""Name every dataset this workspace holds, most recently updated first.
|
|
464
471
|
|
|
@@ -1838,75 +1845,46 @@ def write(
|
|
|
1838
1845
|
|
|
1839
1846
|
|
|
1840
1847
|
def archive(
|
|
1841
|
-
args: argparse.Namespace, *, client:
|
|
1848
|
+
args: argparse.Namespace, *, client: CloudDatasetAccessClient | None = None
|
|
1842
1849
|
) -> dict[str, Any]:
|
|
1843
|
-
"""
|
|
1844
|
-
|
|
1845
|
-
⚠ THE TYPED TITLE IS COMPARED HERE AS WELL AS BY STUDIO, WHICH IS THE OPPOSITE CHOICE FROM
|
|
1846
|
-
``mr-data table archive`` AND THE REASON IS THAT THE READ IS ALREADY HAPPENING. This write is
|
|
1847
|
-
fenced on the dataset's version, so the page has to be read before it can be sent; the title
|
|
1848
|
-
arrives in the same body, the comparison costs nothing, and the fence is what makes it safe --
|
|
1849
|
-
a title that changed after the read moves the version, and the conditional refuses. Where a
|
|
1850
|
-
read would have to be bought only to give a second opinion, this lane does not buy it.
|
|
1851
|
-
|
|
1852
|
-
⚠ AND IT IS NOT A DELETION. Runs, artifacts, versions and the decision record all stay. What
|
|
1853
|
-
the command ends is the page's claim on its title and its place in the workspace's listing.
|
|
1854
|
-
"""
|
|
1855
|
-
|
|
1850
|
+
"""Use the UI's Cloud archive ceremony; a Studio-only tombstone is incomplete."""
|
|
1856
1851
|
dataset_id = identifier(getattr(args, "dataset_id", None) or "", "dataset identifier")
|
|
1857
1852
|
confirmation = getattr(args, "confirm_name", None)
|
|
1858
1853
|
if not isinstance(confirmation, str) or not confirmation.strip():
|
|
1859
1854
|
raise ThinLaneError(
|
|
1860
1855
|
"THIN_REQUEST_INVALID",
|
|
1861
1856
|
"mr-data dataset archive needs the dataset's current title typed out: "
|
|
1862
|
-
|
|
1863
|
-
"holds",
|
|
1857
|
+
"--confirm-name TITLE",
|
|
1864
1858
|
)
|
|
1865
1859
|
stated = confirmation.strip()
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1860
|
+
if client is None:
|
|
1861
|
+
credentials = resolve_cloud_credentials()
|
|
1862
|
+
require_device_key(credentials)
|
|
1863
|
+
selected = CloudDatasetAccessClient(credentials)
|
|
1864
|
+
elif isinstance(client, CloudDatasetAccessClient):
|
|
1865
|
+
selected = client
|
|
1866
|
+
else:
|
|
1871
1867
|
raise ThinLaneError(
|
|
1872
|
-
"
|
|
1873
|
-
f"this dataset is called {held!r} and --confirm-name said {stated!r}. Archiving "
|
|
1874
|
-
"releases the title, so the title is what confirms it: type the one on the page, or "
|
|
1875
|
-
"check you have the dataset you meant",
|
|
1876
|
-
)
|
|
1877
|
-
try:
|
|
1878
|
-
archived = selected.archive_dataset(
|
|
1879
|
-
dataset_id,
|
|
1880
|
-
body={
|
|
1881
|
-
"schema_version": COMMAND_REQUEST_VERSION_FLOOR,
|
|
1882
|
-
"workspace_id": str(selected.session.workspace_id),
|
|
1883
|
-
"confirmation_name": stated,
|
|
1884
|
-
},
|
|
1885
|
-
expected_version=version,
|
|
1868
|
+
"THIN_REQUEST_INVALID", "dataset archive requires the Cloud archive client"
|
|
1886
1869
|
)
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
# page under a title that is no longer its own. What it gets instead is the same sentence
|
|
1895
|
-
# the other two end on, because it is the same thing that happened.
|
|
1870
|
+
answer = selected.archive_dataset(dataset_id, stated)
|
|
1871
|
+
if (
|
|
1872
|
+
answer.get("schema_version") != "mostlyright-dataset-archive.v1"
|
|
1873
|
+
or answer.get("status") != "dataset_archived"
|
|
1874
|
+
or answer.get("dataset_id") != dataset_id
|
|
1875
|
+
or answer.get("name") != stated
|
|
1876
|
+
):
|
|
1896
1877
|
raise ThinLaneError(
|
|
1897
|
-
"
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
"is still the one you typed, and archive again",
|
|
1901
|
-
) from refusal
|
|
1878
|
+
"THIN_RESPONSE_INVALID",
|
|
1879
|
+
"Cloud did not confirm this dataset's complete archive",
|
|
1880
|
+
)
|
|
1902
1881
|
return {
|
|
1903
1882
|
"schema_version": ARCHIVE_SCHEMA,
|
|
1904
1883
|
"status": "dataset_archived",
|
|
1905
1884
|
"lane": "hosted",
|
|
1906
|
-
"dataset_id":
|
|
1885
|
+
"dataset_id": dataset_id,
|
|
1907
1886
|
"name": stated,
|
|
1908
|
-
"
|
|
1909
|
-
"dashboard_url": dataset_dashboard_url(selected.session.cloud_url, dataset_id),
|
|
1887
|
+
"dashboard_url": dataset_dashboard_url(selected.cloud_url, dataset_id),
|
|
1910
1888
|
"note": (
|
|
1911
1889
|
f"The dataset is retired and the title {stated!r} is free for another one. Nothing "
|
|
1912
1890
|
"built was deleted: every run, artifact, version and decision cell stays where it is."
|
|
@@ -2488,7 +2466,6 @@ def watch(
|
|
|
2488
2466
|
}
|
|
2489
2467
|
|
|
2490
2468
|
|
|
2491
|
-
|
|
2492
2469
|
def listing(
|
|
2493
2470
|
args: argparse.Namespace, *, client: CloudDatasetAccessClient | None = None
|
|
2494
2471
|
) -> dict[str, Any]:
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_public.py
RENAMED
|
@@ -867,6 +867,20 @@ CLOUD_PUBLIC_TABLE_DOWNLOAD_URL_PATH = "/api/cli/public/tables/{table_id}/downlo
|
|
|
867
867
|
QUERY_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-public-query.v1"
|
|
868
868
|
DOWNLOAD_URL_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-public-download-url.v1"
|
|
869
869
|
|
|
870
|
+
#: Cloud bounds one credentialled query answer at 10 000 rows of at most 64 columns, and at 8 MiB
|
|
871
|
+
#: of JSON for the rows. The ceilings here admit the widest page those bounds allow: the row bytes
|
|
872
|
+
#: plus room for the members around them, and one member per value, per row and per member around
|
|
873
|
+
#: the rows.
|
|
874
|
+
MAX_PUBLIC_QUERY_ROWS = 10_000
|
|
875
|
+
MAX_PUBLIC_QUERY_COLUMNS = 64
|
|
876
|
+
MAX_PUBLIC_QUERY_ROW_BYTES = 8 * 1024 * 1024
|
|
877
|
+
MAX_PUBLIC_QUERY_ENVELOPE_BYTES = 1024 * 1024
|
|
878
|
+
MAX_PUBLIC_QUERY_ENVELOPE_MEMBERS = 16_384
|
|
879
|
+
MAX_PUBLIC_QUERY_RESPONSE_BYTES = MAX_PUBLIC_QUERY_ROW_BYTES + MAX_PUBLIC_QUERY_ENVELOPE_BYTES
|
|
880
|
+
MAX_PUBLIC_QUERY_RESPONSE_MEMBERS = (
|
|
881
|
+
MAX_PUBLIC_QUERY_ROWS * (MAX_PUBLIC_QUERY_COLUMNS + 1) + MAX_PUBLIC_QUERY_ENVELOPE_MEMBERS
|
|
882
|
+
)
|
|
883
|
+
|
|
870
884
|
|
|
871
885
|
class PublicQueryClient(CloudClient):
|
|
872
886
|
"""Authenticated public table query + download-url over the device-key client."""
|
|
@@ -878,6 +892,9 @@ class PublicQueryClient(CloudClient):
|
|
|
878
892
|
act="query a public table",
|
|
879
893
|
body=body,
|
|
880
894
|
absent="no public table has that id",
|
|
895
|
+
maximum=MAX_PUBLIC_QUERY_RESPONSE_BYTES,
|
|
896
|
+
table_values=True,
|
|
897
|
+
max_members=MAX_PUBLIC_QUERY_RESPONSE_MEMBERS,
|
|
881
898
|
)
|
|
882
899
|
|
|
883
900
|
def download_url(self, table_id: str) -> dict[str, Any]:
|
|
@@ -1091,6 +1108,8 @@ __all__ = [
|
|
|
1091
1108
|
"FETCH_SCHEMA",
|
|
1092
1109
|
"LOOKUP_SCHEMA",
|
|
1093
1110
|
"MAX_LIMIT",
|
|
1111
|
+
"MAX_PUBLIC_QUERY_RESPONSE_BYTES",
|
|
1112
|
+
"MAX_PUBLIC_QUERY_RESPONSE_MEMBERS",
|
|
1094
1113
|
"MAX_SAMPLE_LIMIT",
|
|
1095
1114
|
"QUERY_SCHEMA",
|
|
1096
1115
|
"SAMPLE_SCHEMA",
|
|
@@ -910,6 +910,11 @@ def _validate_private_file(
|
|
|
910
910
|
def _windows_acl_is_private(path: Path, *, require_confidential: bool = True) -> bool:
|
|
911
911
|
"""Prove only the current user, SYSTEM, or Administrators can mutate a Windows path.
|
|
912
912
|
|
|
913
|
+
The path must be owned by the current user, which is what lets an OWNER RIGHTS entry count as
|
|
914
|
+
the current user: it names whoever owns the path. CPython 3.12.4 and later give a folder made
|
|
915
|
+
with ``mkdir(mode=0o700)`` three entries, for SYSTEM, Administrators and OWNER RIGHTS, and none
|
|
916
|
+
for the user by name.
|
|
917
|
+
|
|
913
918
|
With ``require_confidential`` they must also be the only accounts that can read it. Only the
|
|
914
919
|
explicit secure-file credential needs that, when it is read or staged; replacing or removing it
|
|
915
920
|
exposes nothing. The folder, the lock file, the backend manifest and the DPAPI blob hold no
|
|
@@ -957,7 +962,8 @@ def _windows_acl_is_private(path: Path, *, require_confidential: bool = True) ->
|
|
|
957
962
|
return False
|
|
958
963
|
allowed = [current_sid]
|
|
959
964
|
allocated: list[ctypes.c_void_p] = []
|
|
960
|
-
|
|
965
|
+
# SYSTEM, Administrators, and OWNER RIGHTS, which stands for the owner proved above.
|
|
966
|
+
for text in ("S-1-5-18", "S-1-5-32-544", "S-1-3-4"):
|
|
961
967
|
sid = ctypes.c_void_p()
|
|
962
968
|
if not advapi32.ConvertStringSidToSidW(text, ctypes.byref(sid)) or not sid.value:
|
|
963
969
|
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/boundaries.md
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/commands.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/live-run.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/one-install.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/promote.md
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/receipts.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/skills/mr-data-build/references/transforms.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/agent_protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/key_seam.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/page_coverage.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/session_probes.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/skill_assets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/acquire.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/activity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/approvals.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/categories.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/download.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/narrative.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/parity.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/probe.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/propose.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/recipe_lint.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/research.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/router.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/stream.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/transport.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/user_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_catalog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_handoff.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_query.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_reader.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_runs.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_search.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_secrets.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_stream.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/v4_tables.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/thin/vocabulary.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/__init__.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/attendance.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/clarification.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/cloud_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.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
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/credentials.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/login.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/path_kind.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/plain_file.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/remediation.py
RENAMED
|
File without changes
|
{mostlyright_data-0.29.3 → mostlyright_data-0.29.5}/src/mostlyright/data_harness/ux/render.py
RENAMED
|
File without changes
|