mostlyright-data 0.25.7__tar.gz → 0.25.8__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.25.7 → mostlyright_data-0.25.8}/PKG-INFO +1 -1
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/pyproject.toml +1 -1
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/propose.py +1 -1
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/recipe.py +209 -8
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/router.py +24 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/session.py +5 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_artifacts.py +73 -2
- mostlyright_data-0.25.8/src/mostlyright/data_harness/thin/v4_connected.py +179 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_datasets.py +79 -5
- mostlyright_data-0.25.8/src/mostlyright/data_harness/thin/v4_public.py +1087 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_tables.py +639 -12
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/vocabulary.py +16 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/render.py +19 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/.gitignore +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/README.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/scripts/hatch_build.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/SKILL.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/agents/openai.yaml +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/1-open-the-page-and-the-link-to-it-in-the-first-message.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/2-brief-two-to-four-questions-each-with-a-recommended-answer.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/3-probe-read-a-source-before-committing-to-it.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/4-decide-say-what-you-chose-what-you-refused-and-ask-one-question.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/5-draft-one-recipe-document-one-call.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/6-build-one-run-sized-to-acquire-every-measured-source-whole.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/7-interrogate-ask-the-run-what-it-actually-delivered.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/8-fix-revise-the-document-and-register-it-again.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/9-present-only-what-survived-inspection-with-caveats.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/agent-protocol.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/autonomous-delivery.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/before-the-first-tool-call.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/boundaries.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/cloud-authentication-preflight.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/commands.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/cross-repository-protocol-reference.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/installation-parity.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/live-run.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/narrating-the-run.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/not-hosted-yet.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/one-install.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/prediction-labels.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/promote.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/readers.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/receipts.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/recording-a-stream-venue.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/recovering-an-import-failure.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/reference-pages.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/required-protocol.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/source-credentials.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/sources.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/the-one-thing-to-say-about-the-skill-itself.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/transforms.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/user-communication-contract.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/references/writing-a-decision-record.md +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/skills/mr-data-build/scripts/write_research_notebook.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/__init__.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/agent_protocol.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/canonical.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/formats.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/hosted_crawler_protocol.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/key_seam.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/page_coverage.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/part_check_evidence.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/session_probes.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/skill_assets.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/table_manifest.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/__init__.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/acquire.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/acquire_cancel.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/activity.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/approvals.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/categories.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/commands.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/dataset-categories-v1.json +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/download.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/narrative.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/parity.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/probe.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/progress_vocabulary.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/recipe_brief.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/recipe_lint.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/research.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/runs.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/stream.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/stream_venue.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/transport.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/user_agent.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_catalog.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_connections.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_dataset_covers.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_handoff.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_narrative.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_query.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_reader.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_runs.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_secrets.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/v4_stream.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/__init__.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/attendance.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/clarification.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/cloud_auth.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/commands/__init__.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/commands/auth.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/commands/clarify.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/commands/login.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/commands/whoami.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/credential_native.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/credential_store.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/credentials.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/login.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/path_kind.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/plain_file.py +0 -0
- {mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/ux/remediation.py +0 -0
{mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/propose.py
RENAMED
|
@@ -2275,7 +2275,7 @@ def _dataset_conflict_remedy(refusal: ThinLaneError) -> str | None:
|
|
|
2275
2275
|
return (
|
|
2276
2276
|
"a Dataset of this name already exists in the workspace; if an earlier run of this bundle "
|
|
2277
2277
|
'registered it, adopt it by id in dataset.json ({"dataset_id": "..."}) -- '
|
|
2278
|
-
"mr-data list names the workspace's datasets -- or choose another name"
|
|
2278
|
+
"mr-data dataset list names the workspace's datasets -- or choose another name"
|
|
2279
2279
|
)
|
|
2280
2280
|
|
|
2281
2281
|
|
{mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/recipe.py
RENAMED
|
@@ -51,17 +51,23 @@ from mostlyright.data_harness.thin.recipe_lint import lint_document, lint_refusa
|
|
|
51
51
|
from mostlyright.data_harness.thin.session import StudioSession, open_studio_session
|
|
52
52
|
from mostlyright.data_harness.thin.transport import ThinLaneError
|
|
53
53
|
from mostlyright.data_harness.thin.v4 import identifier
|
|
54
|
-
from mostlyright.data_harness.thin.v4_datasets import
|
|
54
|
+
from mostlyright.data_harness.thin.v4_datasets import (
|
|
55
|
+
StudioV4DatasetClient,
|
|
56
|
+
append_cell_body,
|
|
57
|
+
)
|
|
55
58
|
from mostlyright.data_harness.thin.v4_narrative import v4_cell_receipt
|
|
59
|
+
from mostlyright.data_harness.thin.v4_runs import StudioV4RunClient
|
|
60
|
+
from mostlyright.data_harness.thin.v4_runs import start as start_run
|
|
56
61
|
from mostlyright.data_harness.ux.credentials import resolve_cloud_credentials
|
|
57
62
|
from mostlyright.data_harness.ux.plain_file import PlainFileRefusal, read_plain_file
|
|
58
63
|
|
|
59
64
|
#: The one-line help both front doors print for this command.
|
|
60
|
-
COMMAND_HELP = "register one recipe document
|
|
65
|
+
COMMAND_HELP = "register one recipe document, or propose a revision that also starts one replay"
|
|
61
66
|
|
|
62
67
|
REGISTER_SCHEMA = f"{THIN_SCHEMA_PREFIX}-recipe.v1"
|
|
63
68
|
SHOW_SCHEMA = f"{THIN_SCHEMA_PREFIX}-recipe-read.v1"
|
|
64
69
|
READINESS_SCHEMA = f"{THIN_SCHEMA_PREFIX}-recipe-readiness.v1"
|
|
70
|
+
PROPOSE_SCHEMA = f"{THIN_SCHEMA_PREFIX}-recipe-propose.v1"
|
|
65
71
|
|
|
66
72
|
#: The bound on the document. The same number the authoring lane bounded one of its six documents
|
|
67
73
|
#: by -- four times the contract's own 262144-byte ceiling on a canonical recipe -- rather than a
|
|
@@ -126,6 +132,7 @@ UNREFRESHABLE_PLAN_TAIL = (
|
|
|
126
132
|
#: `mr-data recipe show ID` is how a person says it out loud.
|
|
127
133
|
SHOW = "show"
|
|
128
134
|
READINESS = "readiness"
|
|
135
|
+
PROPOSE = "propose"
|
|
129
136
|
# Studio evaluates the exact persisted admission proof for each recipe revision. Keep this
|
|
130
137
|
# inventory page small enough that a normal production workspace completes each bounded request
|
|
131
138
|
# inside the thin client's request deadline, while preserving the established 40,000-item
|
|
@@ -148,15 +155,17 @@ def declare_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
148
155
|
metavar="FILE",
|
|
149
156
|
help=(
|
|
150
157
|
"the recipe document to register, as a path to a JSON file; the word `show`, "
|
|
151
|
-
"followed by a recipe identifier, to read one back;
|
|
152
|
-
"whether current tables may schedule a bounded incremental refresh"
|
|
158
|
+
"followed by a recipe identifier, to read one back; `readiness` to inventory "
|
|
159
|
+
"whether current tables may schedule a bounded incremental refresh; or `propose`, "
|
|
160
|
+
"followed by a recipe document path and `--sources-from RUN_ID`, to register a "
|
|
161
|
+
"revision and start exactly one retained-input replay (MCP `propose_revision`)"
|
|
153
162
|
),
|
|
154
163
|
)
|
|
155
164
|
parser.add_argument(
|
|
156
165
|
"recipe_id",
|
|
157
166
|
nargs="?",
|
|
158
167
|
metavar="RECIPE_ID",
|
|
159
|
-
help="with `show`, the recipe to read back",
|
|
168
|
+
help="with `show`, the recipe to read back; with `propose`, the recipe document path",
|
|
160
169
|
)
|
|
161
170
|
parser.add_argument(
|
|
162
171
|
"--no-lint",
|
|
@@ -171,6 +180,18 @@ def declare_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
171
180
|
help="record that the user delegated the brief, in their own words, and register; this "
|
|
172
181
|
"is how `you choose` or `just build it` is written onto the dataset as the answer it is",
|
|
173
182
|
)
|
|
183
|
+
parser.add_argument(
|
|
184
|
+
"--sources-from",
|
|
185
|
+
dest="sources_from_run",
|
|
186
|
+
metavar="RUN_ID",
|
|
187
|
+
help="with `propose`, replay the retained raw inputs of this successful run; required, "
|
|
188
|
+
"and replay never becomes live",
|
|
189
|
+
)
|
|
190
|
+
parser.add_argument(
|
|
191
|
+
"--confirm",
|
|
192
|
+
action="store_true",
|
|
193
|
+
help="with `propose`, authorize the projected spend if the replay is held for one",
|
|
194
|
+
)
|
|
174
195
|
|
|
175
196
|
|
|
176
197
|
#: The flags that describe a registration. ``show`` reads one back and neither can mean anything
|
|
@@ -181,6 +202,13 @@ _REGISTER_ONLY_FLAGS: tuple[tuple[str, str], ...] = (
|
|
|
181
202
|
("delegated", "--delegated"),
|
|
182
203
|
)
|
|
183
204
|
|
|
205
|
+
#: Flags that belong only to ``propose``. A plain registration (or show/readiness) that carries
|
|
206
|
+
#: one is refused by name rather than silently dropped -- same construction as _REGISTER_ONLY_FLAGS.
|
|
207
|
+
_PROPOSE_ONLY_FLAGS: tuple[tuple[str, str], ...] = (
|
|
208
|
+
("sources_from_run", "--sources-from"),
|
|
209
|
+
("confirm", "--confirm"),
|
|
210
|
+
)
|
|
211
|
+
|
|
184
212
|
|
|
185
213
|
def _session(args: argparse.Namespace) -> StudioSession:
|
|
186
214
|
return open_studio_session(resolve_cloud_credentials())
|
|
@@ -1159,6 +1187,163 @@ def readiness(
|
|
|
1159
1187
|
}
|
|
1160
1188
|
|
|
1161
1189
|
|
|
1190
|
+
def propose(
|
|
1191
|
+
args: argparse.Namespace,
|
|
1192
|
+
*,
|
|
1193
|
+
client: StudioV4DatasetClient | None = None,
|
|
1194
|
+
run_client: StudioV4RunClient | None = None,
|
|
1195
|
+
) -> dict[str, Any]:
|
|
1196
|
+
"""``mr-data recipe propose FILE --sources-from RUN``: register + start one replay.
|
|
1197
|
+
|
|
1198
|
+
Mirrors cloud MCP ``propose_revision``: the revision is durable even when the retained-input
|
|
1199
|
+
replay is held for spend confirmation or refused. Validate ``--sources-from`` before
|
|
1200
|
+
registration so a bad run id never leaves an orphan revision. A non-held replay refusal
|
|
1201
|
+
returns ``revision_registered_replay_not_started`` with the immutable coordinates; a held
|
|
1202
|
+
replay returns outer status ``held`` (exit 2) so a script does not treat spend-gate as success.
|
|
1203
|
+
"""
|
|
1204
|
+
|
|
1205
|
+
document_path = getattr(args, "recipe_id", None)
|
|
1206
|
+
if not isinstance(document_path, str) or not document_path:
|
|
1207
|
+
raise ThinLaneError(
|
|
1208
|
+
"THIN_REQUEST_INVALID",
|
|
1209
|
+
"mr-data recipe propose needs the recipe document path after the word propose",
|
|
1210
|
+
)
|
|
1211
|
+
sources_raw = getattr(args, "sources_from_run", None)
|
|
1212
|
+
if not isinstance(sources_raw, str) or not sources_raw:
|
|
1213
|
+
raise ThinLaneError(
|
|
1214
|
+
"THIN_REQUEST_INVALID",
|
|
1215
|
+
"mr-data recipe propose needs --sources-from RUN_ID naming the "
|
|
1216
|
+
"retained inputs to replay",
|
|
1217
|
+
)
|
|
1218
|
+
# Validate before register — a bad identifier must not leave a durable revision behind.
|
|
1219
|
+
sources_from = identifier(sources_raw, "source run identifier")
|
|
1220
|
+
|
|
1221
|
+
register_args = argparse.Namespace(**vars(args))
|
|
1222
|
+
register_args.target = document_path
|
|
1223
|
+
register_args.recipe_id = None
|
|
1224
|
+
registration = register(register_args, client=client)
|
|
1225
|
+
|
|
1226
|
+
coordinates = {
|
|
1227
|
+
"recipe_id": registration["recipe_id"],
|
|
1228
|
+
"recipe_digest": registration["recipe_digest"],
|
|
1229
|
+
"dataset_id": registration["dataset_id"],
|
|
1230
|
+
"table_id": registration["table_id"],
|
|
1231
|
+
"source_ids": registration["source_ids"],
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
run_args = argparse.Namespace(
|
|
1235
|
+
recipe=registration["recipe_id"],
|
|
1236
|
+
digest=registration["recipe_digest"],
|
|
1237
|
+
mode="replay",
|
|
1238
|
+
sources_from_run=sources_from,
|
|
1239
|
+
max_rows=None,
|
|
1240
|
+
max_source_bytes=None,
|
|
1241
|
+
window=None,
|
|
1242
|
+
resource_class=None,
|
|
1243
|
+
progressive=False,
|
|
1244
|
+
resume_capture_run_id=None,
|
|
1245
|
+
confirm=bool(getattr(args, "confirm", False)),
|
|
1246
|
+
confirm_held=None,
|
|
1247
|
+
cancel=None,
|
|
1248
|
+
retry=None,
|
|
1249
|
+
approve_full=None,
|
|
1250
|
+
)
|
|
1251
|
+
try:
|
|
1252
|
+
replay = start_run(run_args, client=run_client)
|
|
1253
|
+
except ThinLaneError as refusal:
|
|
1254
|
+
payload: dict[str, Any] = {
|
|
1255
|
+
"schema_version": PROPOSE_SCHEMA,
|
|
1256
|
+
"status": "revision_registered_replay_not_started",
|
|
1257
|
+
"lane": "hosted",
|
|
1258
|
+
**coordinates,
|
|
1259
|
+
"sources_from_run": sources_from,
|
|
1260
|
+
"refusal": {"code": refusal.code, "detail": refusal.detail},
|
|
1261
|
+
"idempotent_by": registration.get("idempotent_by", "recipe_digest"),
|
|
1262
|
+
"brief": registration["brief"],
|
|
1263
|
+
"next": (
|
|
1264
|
+
"The recipe revision was registered, but its retained-input replay did not "
|
|
1265
|
+
"start. Keep these immutable coordinates and correct the replay precondition "
|
|
1266
|
+
"before proposing again."
|
|
1267
|
+
),
|
|
1268
|
+
}
|
|
1269
|
+
_copy_registration_extras(registration, payload)
|
|
1270
|
+
return payload
|
|
1271
|
+
|
|
1272
|
+
if replay.get("status") == "run_awaiting_confirmation":
|
|
1273
|
+
held: dict[str, Any] = {
|
|
1274
|
+
"schema_version": PROPOSE_SCHEMA,
|
|
1275
|
+
"status": "held",
|
|
1276
|
+
"lane": "hosted",
|
|
1277
|
+
**coordinates,
|
|
1278
|
+
"sources_from_run": sources_from,
|
|
1279
|
+
"mode": "replay",
|
|
1280
|
+
"idempotent_by": registration.get("idempotent_by", "recipe_digest"),
|
|
1281
|
+
"brief": registration["brief"],
|
|
1282
|
+
"replay": replay,
|
|
1283
|
+
"next": (
|
|
1284
|
+
"Show the projected cost to the user, then call "
|
|
1285
|
+
f"`{replay.get('confirm_command', 'mr-data run --confirm …')}` only if they "
|
|
1286
|
+
"agree. The replay produces repair evidence only; no full run is approved."
|
|
1287
|
+
),
|
|
1288
|
+
}
|
|
1289
|
+
for key in (
|
|
1290
|
+
"run_id",
|
|
1291
|
+
"projected_bytes",
|
|
1292
|
+
"projected_runtime_s",
|
|
1293
|
+
"projected_cost",
|
|
1294
|
+
"confirm_command",
|
|
1295
|
+
"build_scope",
|
|
1296
|
+
):
|
|
1297
|
+
if key in replay:
|
|
1298
|
+
held[key] = replay[key]
|
|
1299
|
+
if "run_id" in held:
|
|
1300
|
+
held["replay_run_id"] = held["run_id"]
|
|
1301
|
+
_copy_registration_extras(registration, held)
|
|
1302
|
+
return held
|
|
1303
|
+
|
|
1304
|
+
payload = {
|
|
1305
|
+
"schema_version": PROPOSE_SCHEMA,
|
|
1306
|
+
"status": "recipe_proposed",
|
|
1307
|
+
"lane": "hosted",
|
|
1308
|
+
**coordinates,
|
|
1309
|
+
"sources_from_run": sources_from,
|
|
1310
|
+
"replay": replay,
|
|
1311
|
+
"idempotent_by": registration.get("idempotent_by", "recipe_digest"),
|
|
1312
|
+
"brief": registration["brief"],
|
|
1313
|
+
}
|
|
1314
|
+
run_body = replay.get("run") if isinstance(replay.get("run"), Mapping) else None
|
|
1315
|
+
if isinstance(run_body, Mapping) and isinstance(run_body.get("run_id"), str):
|
|
1316
|
+
payload["replay_run_id"] = run_body["run_id"]
|
|
1317
|
+
_copy_registration_extras(registration, payload)
|
|
1318
|
+
return payload
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
def _copy_registration_extras(
|
|
1322
|
+
registration: Mapping[str, Any], payload: dict[str, Any]
|
|
1323
|
+
) -> None:
|
|
1324
|
+
"""Carry optional register fields onto a propose receipt without inventing them."""
|
|
1325
|
+
|
|
1326
|
+
for key in (
|
|
1327
|
+
"warnings",
|
|
1328
|
+
"columns_without_description",
|
|
1329
|
+
"sources_without_continuation",
|
|
1330
|
+
"delegation_cell",
|
|
1331
|
+
):
|
|
1332
|
+
if key in registration:
|
|
1333
|
+
payload[key] = registration[key]
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
def propose_exit_code(payload: Mapping[str, Any]) -> int:
|
|
1337
|
+
"""Held or failed replay after a durable register is a gate, not a successful propose."""
|
|
1338
|
+
|
|
1339
|
+
status = payload.get("status")
|
|
1340
|
+
if status in {"held", "revision_registered_replay_not_started"}:
|
|
1341
|
+
return 2
|
|
1342
|
+
return 0
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
|
|
1346
|
+
|
|
1162
1347
|
def recipe(
|
|
1163
1348
|
args: argparse.Namespace, *, client: StudioV4DatasetClient | None = None
|
|
1164
1349
|
) -> dict[str, Any]:
|
|
@@ -1174,7 +1359,7 @@ def recipe(
|
|
|
1174
1359
|
if args.target in {SHOW, READINESS}:
|
|
1175
1360
|
given = [
|
|
1176
1361
|
flag
|
|
1177
|
-
for destination, flag in _REGISTER_ONLY_FLAGS
|
|
1362
|
+
for destination, flag in (*_REGISTER_ONLY_FLAGS, *_PROPOSE_ONLY_FLAGS)
|
|
1178
1363
|
if getattr(args, destination, None) not in (None, False)
|
|
1179
1364
|
]
|
|
1180
1365
|
if given:
|
|
@@ -1191,12 +1376,25 @@ def recipe(
|
|
|
1191
1376
|
"mr-data recipe readiness takes no recipe identifier",
|
|
1192
1377
|
)
|
|
1193
1378
|
return readiness(args, client=client)
|
|
1379
|
+
if args.target == PROPOSE:
|
|
1380
|
+
return propose(args, client=client)
|
|
1381
|
+
propose_only = [
|
|
1382
|
+
flag
|
|
1383
|
+
for destination, flag in _PROPOSE_ONLY_FLAGS
|
|
1384
|
+
if getattr(args, destination, None) not in (None, False)
|
|
1385
|
+
]
|
|
1386
|
+
if propose_only:
|
|
1387
|
+
raise ThinLaneError(
|
|
1388
|
+
"THIN_REQUEST_INVALID",
|
|
1389
|
+
f"{', '.join(propose_only)} belong to mr-data recipe propose, not to a plain "
|
|
1390
|
+
"registration; say `propose` before the document path, or drop the flag",
|
|
1391
|
+
)
|
|
1194
1392
|
if args.recipe_id:
|
|
1195
1393
|
raise ThinLaneError(
|
|
1196
1394
|
"THIN_REQUEST_INVALID",
|
|
1197
1395
|
f"mr-data recipe takes one document to register, `show` and a recipe identifier, "
|
|
1198
|
-
f"or `
|
|
1199
|
-
f"{args.target!r} is
|
|
1396
|
+
f"`readiness`, or `propose` and a document path; "
|
|
1397
|
+
f"{args.target!r} is none of those",
|
|
1200
1398
|
)
|
|
1201
1399
|
return register(args, client=client)
|
|
1202
1400
|
|
|
@@ -1210,6 +1408,8 @@ __all__ = [
|
|
|
1210
1408
|
"NAMED_UNCONTINUED_SOURCES",
|
|
1211
1409
|
"NAMED_UNDESCRIBED_COLUMNS",
|
|
1212
1410
|
"NDJSON_SOURCE_BYTE_CEILING",
|
|
1411
|
+
"PROPOSE",
|
|
1412
|
+
"PROPOSE_SCHEMA",
|
|
1213
1413
|
"READER_SOURCE_BYTE_CEILING",
|
|
1214
1414
|
"READINESS",
|
|
1215
1415
|
"READINESS_PAGE_SIZE",
|
|
@@ -1226,6 +1426,7 @@ __all__ = [
|
|
|
1226
1426
|
"columns_without_description",
|
|
1227
1427
|
"continuation_warnings",
|
|
1228
1428
|
"declare_arguments",
|
|
1429
|
+
"propose",
|
|
1229
1430
|
"read_document",
|
|
1230
1431
|
"readiness",
|
|
1231
1432
|
"recipe",
|
{mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/router.py
RENAMED
|
@@ -131,6 +131,12 @@ HOSTED_SUMMARY = dict(
|
|
|
131
131
|
("reader", "ask Studio to normalize reader options for a family and version"),
|
|
132
132
|
("source", "queue or read one bounded source inspection"),
|
|
133
133
|
("catalog", "ask the sealed public-source catalogue what might answer a question"),
|
|
134
|
+
(
|
|
135
|
+
"public",
|
|
136
|
+
"read the public dataset catalogue anonymously "
|
|
137
|
+
"(search, show, tables, schema, sample)",
|
|
138
|
+
),
|
|
139
|
+
("connected", "list or connect public datasets for this workspace (device key)"),
|
|
134
140
|
("stream", "record a public wss venue and name the recording a build reads"),
|
|
135
141
|
("keys", "store a source credential by name, name the stored ones, and withdraw one"),
|
|
136
142
|
("open", "hand this terminal's session to the browser as a single-use address"),
|
|
@@ -152,6 +158,7 @@ HOSTED_SUMMARY = dict(
|
|
|
152
158
|
("demote", "ask Studio to withdraw the live pointer from one table or many"),
|
|
153
159
|
("watch", "stream one run's live progress, resuming across stream cuts"),
|
|
154
160
|
("download", "bring one run's artifacts back here, each one's digest checked"),
|
|
161
|
+
("artifacts", "list what one run sealed, without fetching bytes"),
|
|
155
162
|
("receipt", "print the record one run sealed about how it was built"),
|
|
156
163
|
("checks", "report how each check the recipe declared came out for one run"),
|
|
157
164
|
("note", "write one cell of this run's decision record, or list what is written"),
|
|
@@ -263,6 +270,7 @@ def _hosted_parser() -> argparse.ArgumentParser:
|
|
|
263
270
|
from mostlyright.data_harness.thin import recipe as recipe_module
|
|
264
271
|
from mostlyright.data_harness.thin import (
|
|
265
272
|
v4_artifacts,
|
|
273
|
+
v4_connected,
|
|
266
274
|
v4_connections,
|
|
267
275
|
v4_dataset_covers,
|
|
268
276
|
v4_datasets,
|
|
@@ -299,6 +307,13 @@ def _hosted_parser() -> argparse.ArgumentParser:
|
|
|
299
307
|
|
|
300
308
|
v4_catalog.declare_arguments(_add("catalog"))
|
|
301
309
|
|
|
310
|
+
from mostlyright.data_harness.thin import v4_public
|
|
311
|
+
|
|
312
|
+
v4_public.declare_arguments(_add("public"))
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
v4_connected.declare_arguments(_add("connected"))
|
|
316
|
+
|
|
302
317
|
# ------------------------------------------------------------------------------------
|
|
303
318
|
# The source whose bytes do not exist until somebody records them
|
|
304
319
|
# ------------------------------------------------------------------------------------
|
|
@@ -404,11 +419,13 @@ def _handlers() -> dict[str, Callable[[argparse.Namespace], dict[str, Any]]]:
|
|
|
404
419
|
stream_venue,
|
|
405
420
|
v4_artifacts,
|
|
406
421
|
v4_catalog,
|
|
422
|
+
v4_connected,
|
|
407
423
|
v4_connections,
|
|
408
424
|
v4_dataset_covers,
|
|
409
425
|
v4_datasets,
|
|
410
426
|
v4_handoff,
|
|
411
427
|
v4_narrative,
|
|
428
|
+
v4_public,
|
|
412
429
|
v4_query,
|
|
413
430
|
v4_reader,
|
|
414
431
|
v4_runs,
|
|
@@ -442,6 +459,8 @@ def _handlers() -> dict[str, Callable[[argparse.Namespace], dict[str, Any]]]:
|
|
|
442
459
|
# around the command rather than around its one verb, because the verb is a
|
|
443
460
|
# positional and the module is where a second one would land.
|
|
444
461
|
"catalog": v4_catalog.catalog,
|
|
462
|
+
"public": v4_public.public,
|
|
463
|
+
"connected": v4_connected.connected,
|
|
445
464
|
# The venue lane. One name over eight verbs, because registering a document,
|
|
446
465
|
# admitting it, probing it, capturing it, subscribing to it and reading back what
|
|
447
466
|
# any of those produced are steps of one act -- and eight top-level names would be
|
|
@@ -453,6 +472,7 @@ def _handlers() -> dict[str, Callable[[argparse.Namespace], dict[str, Any]]]:
|
|
|
453
472
|
"watch": v4_stream.watch,
|
|
454
473
|
"status": v4_runs.status,
|
|
455
474
|
"download": v4_artifacts.download,
|
|
475
|
+
"artifacts": v4_artifacts.artifacts,
|
|
456
476
|
"verify": v4_artifacts.verify,
|
|
457
477
|
"receipt": v4_artifacts.receipt,
|
|
458
478
|
"checks": v4_artifacts.checks,
|
|
@@ -488,8 +508,10 @@ def _exit_codes() -> dict[str, Callable[[dict[str, Any]], int]]:
|
|
|
488
508
|
"""
|
|
489
509
|
|
|
490
510
|
from mostlyright.data_harness.thin import probe as probe_module
|
|
511
|
+
from mostlyright.data_harness.thin import recipe as recipe_module
|
|
491
512
|
from mostlyright.data_harness.thin import (
|
|
492
513
|
v4_artifacts,
|
|
514
|
+
v4_connected,
|
|
493
515
|
v4_datasets,
|
|
494
516
|
v4_query,
|
|
495
517
|
v4_runs,
|
|
@@ -508,6 +530,8 @@ def _exit_codes() -> dict[str, Callable[[dict[str, Any]], int]]:
|
|
|
508
530
|
"table": v4_tables.lifecycle_exit_code,
|
|
509
531
|
"demote": v4_tables.lifecycle_exit_code,
|
|
510
532
|
"dataset": v4_datasets.dataset_exit_code,
|
|
533
|
+
"recipe": recipe_module.propose_exit_code,
|
|
534
|
+
"connected": v4_connected.connected_exit_code,
|
|
511
535
|
}
|
|
512
536
|
|
|
513
537
|
|
{mostlyright_data-0.25.7 → mostlyright_data-0.25.8}/src/mostlyright/data_harness/thin/session.py
RENAMED
|
@@ -63,6 +63,10 @@ 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
|
+
# Workspace dataset directory for agents (`mr-data dataset list`), same facts MCP's
|
|
67
|
+
# `list_my_datasets` reads from Cloud's own table.
|
|
68
|
+
CLOUD_DATASETS_LIST_PATH = "/api/cli/datasets"
|
|
69
|
+
|
|
66
70
|
# Projection repair after a Studio metadata write, without a publication mutation.
|
|
67
71
|
CLOUD_DATASET_IDENTITY_PATH = "/api/cli/datasets/{dataset_id}/identity"
|
|
68
72
|
|
|
@@ -530,6 +534,7 @@ class CloudClient:
|
|
|
530
534
|
|
|
531
535
|
__all__ = [
|
|
532
536
|
"CLOUD_APPROVAL_DASHBOARD_PATH",
|
|
537
|
+
"CLOUD_DATASETS_LIST_PATH",
|
|
533
538
|
"CLOUD_DATASET_ACCESS_PATH",
|
|
534
539
|
"CLOUD_DATASET_DASHBOARD_PATH",
|
|
535
540
|
"CLOUD_DATASET_RUN_QUERY_KEY",
|
|
@@ -83,6 +83,7 @@ from mostlyright.data_harness.thin.v4_stream import check_results, terminal_even
|
|
|
83
83
|
from mostlyright.data_harness.ux.credentials import resolve_cloud_credentials
|
|
84
84
|
|
|
85
85
|
DOWNLOAD_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-download.v1"
|
|
86
|
+
ARTIFACTS_LIST_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-artifacts-list.v1"
|
|
86
87
|
RECEIPT_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-receipt.v1"
|
|
87
88
|
CHECKS_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-checks.v1"
|
|
88
89
|
PEEK_SCHEMA = f"{THIN_SCHEMA_PREFIX}-v4-peek.v1"
|
|
@@ -176,6 +177,7 @@ VERSION_KINDS: tuple[str, ...] = ("table_manifest", "table_part")
|
|
|
176
177
|
#: The one-line help each front door prints, keyed by name so both parsers read one declaration.
|
|
177
178
|
COMMAND_HELP: dict[str, str] = {
|
|
178
179
|
"download": "bring one run's artifacts back to this computer, each one's digest checked",
|
|
180
|
+
"artifacts": "list what one run sealed, without fetching bytes (MCP run_artifacts)",
|
|
179
181
|
"verify": "check one run's sealed table against its manifest, part by part with --deep",
|
|
180
182
|
"receipt": "print the record one run sealed: its sources, its coverage and its raw snapshot",
|
|
181
183
|
"checks": "report how each check the recipe declared came out for one run",
|
|
@@ -185,7 +187,7 @@ COMMAND_HELP: dict[str, str] = {
|
|
|
185
187
|
#: ``peek`` and ``diff`` are NOT here: both already have a declaration in each front door and an
|
|
186
188
|
#: argument surface a script may be carrying, and this plan re-points their handler rather than
|
|
187
189
|
#: re-declaring them.
|
|
188
|
-
COMMANDS: tuple[str, ...] = ("download", "receipt", "checks")
|
|
190
|
+
COMMANDS: tuple[str, ...] = ("download", "artifacts", "receipt", "checks")
|
|
189
191
|
|
|
190
192
|
#: ``verify``, mounted on the FRONT DOOR ALONE and deliberately not through :data:`COMMANDS`.
|
|
191
193
|
#:
|
|
@@ -350,6 +352,19 @@ def declare_arguments(command: str, parser: argparse.ArgumentParser) -> None:
|
|
|
350
352
|
"many, never a folder holding some of the table."
|
|
351
353
|
)
|
|
352
354
|
return
|
|
355
|
+
if command == "artifacts":
|
|
356
|
+
parser.add_argument(
|
|
357
|
+
"action",
|
|
358
|
+
choices=("list",),
|
|
359
|
+
help="list — name every artifact this run sealed (MCP run_artifacts parity)",
|
|
360
|
+
)
|
|
361
|
+
parser.add_argument("run_id", help=_RUN_POSITIONAL)
|
|
362
|
+
parser.description = (
|
|
363
|
+
"List what one run sealed — identifiers, kinds, sizes and digests — without fetching "
|
|
364
|
+
"bytes. Same facts cloud MCP `run_artifacts` returns. Use `mr-data download RUN` when "
|
|
365
|
+
"you want the bytes themselves."
|
|
366
|
+
)
|
|
367
|
+
return
|
|
353
368
|
parser.add_argument("run_id", help=_RUN_POSITIONAL)
|
|
354
369
|
|
|
355
370
|
|
|
@@ -509,7 +524,10 @@ def _document(
|
|
|
509
524
|
def _manifest_from_bytes(raw: bytes) -> Any:
|
|
510
525
|
"""One sealed manifest, read through the format's own reader."""
|
|
511
526
|
|
|
512
|
-
from mostlyright.data_harness.table_manifest import
|
|
527
|
+
from mostlyright.data_harness.table_manifest import (
|
|
528
|
+
TableManifest,
|
|
529
|
+
TableManifestError,
|
|
530
|
+
)
|
|
513
531
|
|
|
514
532
|
try:
|
|
515
533
|
return TableManifest.from_bytes(raw)
|
|
@@ -538,6 +556,57 @@ def _head(
|
|
|
538
556
|
}
|
|
539
557
|
|
|
540
558
|
|
|
559
|
+
|
|
560
|
+
_ARTIFACT_LIST_MEMBERS: tuple[str, ...] = (
|
|
561
|
+
"artifact_id",
|
|
562
|
+
"kind",
|
|
563
|
+
"size_bytes",
|
|
564
|
+
"digest",
|
|
565
|
+
"media_type",
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _artifact_row(row: Mapping[str, Any]) -> dict[str, Any]:
|
|
570
|
+
"""The five members MCP ``run_artifacts`` prints; nothing else leaves this lane."""
|
|
571
|
+
|
|
572
|
+
return {member: row[member] for member in _ARTIFACT_LIST_MEMBERS if member in row}
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def artifacts(
|
|
576
|
+
args: argparse.Namespace, *, client: StudioV4ArtifactClient | None = None
|
|
577
|
+
) -> dict[str, Any]:
|
|
578
|
+
"""``mr-data artifacts list RUN``: what this run sealed, without fetching bytes.
|
|
579
|
+
|
|
580
|
+
MCP ``run_artifacts`` parity. Reuses the same listing client ``download`` already follows,
|
|
581
|
+
including cursor aggregation, so a run whose artifacts span pages is still reported whole.
|
|
582
|
+
"""
|
|
583
|
+
|
|
584
|
+
action = getattr(args, "action", None)
|
|
585
|
+
if action != "list":
|
|
586
|
+
raise ThinLaneError(
|
|
587
|
+
"THIN_REQUEST_INVALID",
|
|
588
|
+
"mr-data artifacts takes the action `list` followed by a run identifier",
|
|
589
|
+
)
|
|
590
|
+
run_id = identifier(args.run_id, "run identifier")
|
|
591
|
+
selected = client or _client(args)
|
|
592
|
+
listing = _listing(selected, run_id)
|
|
593
|
+
rows = [_artifact_row(row) for row in listing]
|
|
594
|
+
return {
|
|
595
|
+
"schema_version": ARTIFACTS_LIST_SCHEMA,
|
|
596
|
+
"status": "artifacts_listed",
|
|
597
|
+
**_head(selected.session, run_id),
|
|
598
|
+
"count": len(rows),
|
|
599
|
+
"complete": True,
|
|
600
|
+
"artifacts": _numbered("artifact", rows),
|
|
601
|
+
"next": (
|
|
602
|
+
"this run has sealed nothing yet; ask mr-data status RUN for its state"
|
|
603
|
+
if not rows
|
|
604
|
+
else "mr-data download RUN brings the bytes back; cite each digest"
|
|
605
|
+
),
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
|
|
541
610
|
# --------------------------------------------------------------------------------------------
|
|
542
611
|
# download
|
|
543
612
|
# --------------------------------------------------------------------------------------------
|
|
@@ -1545,6 +1614,7 @@ def _summary(
|
|
|
1545
1614
|
|
|
1546
1615
|
|
|
1547
1616
|
__all__ = [
|
|
1617
|
+
"ARTIFACTS_LIST_SCHEMA",
|
|
1548
1618
|
"CHECKS_SCHEMA",
|
|
1549
1619
|
"CHECK_SCOPE_VERSION",
|
|
1550
1620
|
"COMMANDS",
|
|
@@ -1566,6 +1636,7 @@ __all__ = [
|
|
|
1566
1636
|
"VERIFY_SCHEMA",
|
|
1567
1637
|
"VERSION_KINDS",
|
|
1568
1638
|
"StudioV4ArtifactClient",
|
|
1639
|
+
"artifacts",
|
|
1569
1640
|
"checks",
|
|
1570
1641
|
"checks_exit_code",
|
|
1571
1642
|
"declare_arguments",
|