Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ jobs:
- name: MCP tool reference drift gate
run: uv run python scripts/export_mcp_tools.py --check

- name: Export target catalog drift gate
run: uv run python scripts/export_target_catalog.py --check

# `-n auto` resolves to the runner's core count. The suite has no expensive
# test to remove — ~63 ms mean, no fat tail — so parallelism is the only
# thing that makes this step faster, and it is ~90% of the run's critical
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ nothing was being distributed. This is the first version that is.

### Added

- **Export targets: a release is exported for the model it will train.** Part of the
export-targets epic (#784). Every installed format declares the targets it writes for, and the
catalog is served on every surface: `GET /export-targets`, `visionset target list` and the
`list_export_targets` tool, with `FormatOut.targets` naming each format's own. `POST
/releases/{id}/export` and `GET /releases/{id}/export-compatibility` take `target` beside
`format`, exactly one of the two; `visionset export --target` and the `target` parameter of
`export_release` and `check_export` do the same. A target narrows its format to the geometries
its trainer has a task for - the drop is reported, consented through `allow_lossy`, and honoured
in the output - and the compatibility report, the export result and the job payload all record
`target` beside `format`. The target table in `docs/content/releases.md` is generated from the
catalog by `scripts/export_target_catalog.py`, behind a drift gate.

- **A schema version records which kind of work published it** (#368). New nullable
`provenance` on `AnnotationSchema`: `curated` for a version somebody sat down and designed,
`annotation` for one that fell out of adding a class part-way through labeling. It gates
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ a deliberate manual run, because each costs minutes or needs its own install.
| Generated API client | `pnpm generate:client` (commit the diff) — writes **two** artifacts under `frontend/ui-core/src/generated/`: `api.ts` (the types) and `checks.ts` (the runtime response checks `unwrap` takes). CI diffs the whole directory. | `generated` |
| Wire fixtures (annotator payloads, capability rosters) | `uv run python scripts/export_wire_fixtures.py` (commit the diff) — writes `tests/fixtures/wire_annotations.json` and `tests/fixtures/wire_capabilities.json`, the kernel's answers as bytes for the `frontend` job that installs no Python | part of `python` |
| MCP tool reference | `uv run python scripts/export_mcp_tools.py` (commit the diff) — `docs/content/mcp-tools.md` is generated from the server's own tool listing, because a tool description *is* the interface an agent reads | `generated` |
| Export target catalog | `uv run python scripts/export_target_catalog.py` (commit the diff) — the target table in `docs/content/releases.md` is generated from what the installed formats declare, the same catalog `GET /export-targets` serves | `generated` |

**`scripts/check.sh` runs pytest under `pytest-xdist` with `-n auto`.** The suite is
roughly 3200 tests averaging 63 ms, with only eight over a second — there is no expensive
Expand Down
10 changes: 6 additions & 4 deletions docs/content/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ GET /releases/{release_id}
GET /releases/{release_id}/manifest bytes
GET /releases/{release_id}/verify
GET /releases/{release_id}/assignment
GET /releases/{release_id}/export-compatibility ?format=
POST /releases/{release_id}/export ?format=&allow_lossy=, launch
GET /releases/{release_id}/export-compatibility ?target=|format=, exactly one
POST /releases/{release_id}/export ?target=|format=&allow_lossy=, launch
GET /formats
GET /export-targets the models a release can be exported for

GET /inference/connections
POST /inference/connections
Expand Down Expand Up @@ -834,9 +835,10 @@ each is a decision somebody will otherwise try to "fix":
- **Unknown keys pass.** `additionalProperties: false` constrains what the API *accepts*,
not what it may one day *send*. A client that refused an added field would turn every
backward-compatible release into a broken page.
- **An unknown member of an *open* vocabulary passes.** Seven vocabularies carry
- **An unknown member of an *open* vocabulary passes.** Eight vocabularies carry
`x-visionset-open` in the spec — the four `allowed_actions` sets, `capabilities`,
`SuggestionOut.parameters`, and the reasons a class is left out of a pre-label prompt — and
`SuggestionOut.parameters`, the reasons a class is left out of a pre-label prompt, and the
tasks an export target accepts — and
the generated check for one accepts a member this client
never compiled against, exactly as it accepts an added field. Every other enum still
refuses, and refuses the whole response with it: a value the client must *switch* on has no
Expand Down
23 changes: 15 additions & 8 deletions docs/content/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ visionset job pre-label JOB_ID CONNECTION [--minimum-confidence FLOAT] [--replac
visionset release publish --tag T --project P [--split TRAIN,VAL,TEST] [--seed N]
visionset release list --project P
visionset release verify TAG --project P
visionset export --project P --release TAG --format F --out DIR [--allow-lossy]
visionset export --project P --release TAG --format F --check # writes nothing; exit 1 = it loses something
visionset export --project P --release TAG --target T|--format F --out DIR [--allow-lossy]
visionset export --project P --release TAG --target T|--format F --check # writes nothing; exit 1 = it loses something
visionset format list # no --workspace: it opens nothing
visionset target list # the models a release can be exported for

visionset backfill-thumbnails --project P
visionset token create --name NAME
Expand Down Expand Up @@ -201,9 +202,10 @@ the callback would have to *precede* the subcommand - `visionset --workspace X t
ci` would work and `visionset token create --name ci --workspace X` would fail with "No such
option". Nobody types the first one.

`--json` is per command for the identical reason, and so is every other option here. Three
commands do without `--workspace`, each because it needs none: `visionset format list` reads
installed distributions, which is a fact about the process, and `visionset inference size` asks
`--json` is per command for the identical reason, and so is every other option here. Four
commands do without `--workspace`, each because it needs none: `visionset format list` and
`visionset target list` read installed distributions, which is a fact about the process, and
`visionset inference size` asks
the publishing hub about a model that no row has to name yet; `visionset init` takes a positional
`PATH`,
because it names where to *make* a workspace rather than which one to use — and for that reason it
Expand Down Expand Up @@ -419,9 +421,14 @@ lifecycle must be drivable from a script, not because this is how labelling happ
`release publish --tag T --project P [--split TRAIN,VAL,TEST] [--seed N]` → `ReleaseService.publish`.
`release list --project P`, and `release verify TAG --project P`, whose **exit code is the answer**.

`export --project P --release TAG --format F --out DIR [--allow-lossy]` resolves the format through
the plugin registry and hands the instance to `ReleaseService.export` - the kernel is forbidden from
finding a plugin itself. `format list` says which are installed.
`export --project P --release TAG --target T|--format F --out DIR [--allow-lossy]` resolves the
target or the format through the plugin registry and hands the instance to `ReleaseService.export` -
the kernel is forbidden from finding a plugin itself. `--target` names the model the release will
train and resolves to the format that writes for it; `--format` names a format and addresses no
trainer. Exactly one of the two: both or neither is a usage error at exit 2. `format list` says
which formats are installed and `target list` which models can be trained on their output, each
with the format it resolves to; both take `--json`, whose shape is the wire's. `--format yolo`, the
former name of `ultralytics`, still works for one release and prints a deprecation line on stderr.

A release tag is **case-sensitive** where a project name is not: a tag is an identifier, not a label
somebody reads. `--allow-lossy` is a third gate word beside `--yes` and `--allow-destructive`, never
Expand Down
7 changes: 4 additions & 3 deletions docs/content/mcp-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ error envelope, and the three gate words.

## Always offered

53 tools, in the order an agent meets them: make a project, give it a schema, put images in it, work through them, promote, publish, export.
54 tools, in the order an agent meets them: make a project, give it a schema, put images in it, work through them, promote, publish, export.

| Tool | Takes | What it does |
| --- | --- | --- |
Expand Down Expand Up @@ -59,8 +59,9 @@ error envelope, and the three gate words.
| `list_releases` | `project` | List a project's releases, newest last, with everything each one publishes. |
| `verify_release` | `project`, `tag` | Re-read and re-hash everything a release names, and report what is wrong. |
| `list_formats` | — | List the export formats installed in this VisionSet, and whether each is lossy. |
| `check_export` | `project`, `tag`, `format` | Say what a format would drop from a release, without writing anything. |
| `export_release` | `project`, `tag`, `format`, `dest`, `allow_lossy`? | Write a release to a local directory in one of the installed formats. |
| `list_export_targets` | — | List the models a release can be exported for, each with the format that writes for it. |
| `check_export` | `project`, `tag`, `target`?, `format`? | Say what a target or a format would drop from a release, without writing anything. |
| `export_release` | `project`, `tag`, `dest`, `target`?, `format`?, `allow_lossy`? | Write a release to a local directory, for a target or in one of the installed formats. |
| `list_inference_connections` | — | List this workspace's model connections, oldest first. |
| `model_download_size` | `model_id`, `model_revision` | How big fetching that model's weights would be. Nothing is downloaded. |
| `create_inference_connection` | `name`, `connection_type`, `model_id`, `model_revision`, `device`?, `precision`?, `endpoint_url`?, `provider_id`?, `credential_env`? | Configure a connection. Nothing is downloaded and nothing is contacted. |
Expand Down
16 changes: 9 additions & 7 deletions docs/content/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,10 @@ call until the end. #439 has since added a job gate, but it changes none of this
| `publish_release` | Freeze it under a tag, immutably. |
| `list_releases` | Everything published, with counts and hashes. |
| `verify_release` | Re-hash every blob a release names. |
| `list_formats` | Installed exporters, which are lossy, and what each can write. |
| `check_export` | What a format would drop from a release, before writing anything. |
| `export_release` | Write a release to a local directory. `allow_lossy` where needed. |
| `list_formats` | Installed exporters, which are lossy, what each can write, and the targets it writes for. |
| `list_export_targets` | The models a release can be exported for, each with the format it resolves to and its hints. |
| `check_export` | What a target or a format would drop from a release, before writing anything. Exactly one of `target` and `format`. |
| `export_release` | Write a release to a local directory, for a target or in a format. `allow_lossy` where needed. |

### Inference connections

Expand Down Expand Up @@ -358,15 +359,16 @@ out of the object to pick the variant, and omitting it fails. Always send
## What is not here, and why

Fifty candidate tools were recorded across the four REST tasks; thirty of them shipped and
twenty did not. Twenty-six have been added since, every one of them because a surface grew a
twenty did not. Twenty-seven have been added since, every one of them because a surface grew a
capability an agent had no way to reach. The larger groups say what that looks like: the four
batch-composition tools above; the seven inference-connection tools, closing the Models page's
SDK-first parity; the four schema-draft tools above, because composing a schema across several
calls needs somewhere to hold a class before it is finished; the three deletions, which are
advertised only on request; the pre-labeling trio, `pre_label_job` beside the two fan-outs,
closing the last capability declared with no consumer; and `check_export`, the plan-before-apply
half of an export on the `preview_schema_change` precedent. That is fifty-three offered by
default and fifty-six in all. The parity rule means
closing the last capability declared with no consumer; `check_export`, the plan-before-apply
half of an export on the `preview_schema_change` precedent; and `list_export_targets`, because
`export_release` takes a target name and an agent has to be able to read the catalog it comes
from. That is fifty-four offered by default and fifty-seven in all. The parity rule means
*evaluated*, not *implemented* — tool-selection accuracy degrades with count, so a tool ships
only when an agent has a reason to reach for it that no neighbour covers.

Expand Down
Loading
Loading