Skip to content

feat: "Approve and start" and "Complete and promote" as one action on UI, CLI and MCP - #804

Merged
JArmandoAnaya merged 4 commits into
mainfrom
feat/composed-transitions
Aug 26, 2026
Merged

feat: "Approve and start" and "Complete and promote" as one action on UI, CLI and MCP#804
JArmandoAnaya merged 4 commits into
mainfrom
feat/composed-transitions

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

Closes #790

What changed

Two pairs of lifecycle steps are offered as one action on every surface, without adding a transition to the kernel or a capability to the wire. Each composed action is two sequential calls behind one control, offered on the first step's declaration; if the second step refuses, the batch stays where the first step put it and the surface says so.

CLI. visionset batch approve --start, visionset batch complete --promote and visionset ingest --start (ingest, approve as one job, start). Each performs step one, then step two on success, and prints both outcomes. A refusal of a later step exits non-zero, prints the ordinary typed refusal, and names the step and the batch's actual state, so nobody guesses. --json prints the final batch document; complete --promote --json prints the batch document and the promoted page under batch and promoted, the shape ingest --json already uses for a hand-built composite.

MCP. approve_batch gains start: bool = False and reports started in its payload; complete_batch gains promote: bool = False and reports promoted (a count). Nothing a tool does is an invisible side effect. The approve_batch description no longer tells the caller to invoke start_batch next; it names the parameter. docs/content/mcp-tools.md is regenerated from the served listing, and the capability reachability check is unchanged.

UI. Approve and start sits on a draft's row in the batch table, secondary like every row action, beside Approve (whose dialog remains the way to cut a batch into several jobs), and on the ingest run card once a run settles, where it is the card's one filled control and Open batch steps down to secondary. Complete and promote sits in the gallery header beside Complete, withheld the same way while any frame is still to do, and says what the press moved in the sentence Promote already uses. Approve and start is offered only while the project has an active schema: approval pins one, and the batch's allowed_actions cannot say whether there is one to pin, because that is a fact about the project rather than a state of the batch. Without one, the row keeps its controls and the run card says in words what approving needs, with the way to the schema section beside it; only a schema that does not exist counts as none — any other failure of that read leaves the card as it was.

Every composed control keeps the first step's outcome on screen as a line (Approved against v3, Completed, finishing 1 job) and renders a second-step refusal beneath it through the shared vocabulary, never in its place. The control keeps its own record of having been pressed, because the batch's declaration moves on after the first step and the control would otherwise unmount with it, taking the line and the refusal along.

Docs. docs/content/cli.md, mcp.md and batches.md describe the composed forms; tutorial.md's approve-then-start step becomes one command, with the two-command walk kept as the explicit variant, and its finish becomes complete --promote; ui.md gains Two steps as one. The sitemap is unchanged: no route, tab or entry point moved.

Capabilities checklist

Per control: a refusal renders through refusalProse and FieldError; success renders as the line and, for promotion, the summary and the link to the dataset; the rejected promise is the mutation's own isError; the hooks already invalidate the batch, its jobs, the batch list, the project and the dataset after each step. No legality is computed on the client — the controls are gated on the wire's approve and complete declarations. The schema pre-read is the one condition the declaration cannot carry, and the no-schema card explains the absence rather than hiding it.

Revoked guards

None. Every existing test passes unchanged.

Test plan

  • scripts/check.sh in full on the branch before the rebase: annotator 1071, ui-core 1356, app 11 tests; frontend lint; OpenAPI, generated-client and MCP tool-reference drift; version sync; annotator + app e2e (chromium) 282 passed; browser cycle, real server (chromium) 1 passed. The docs site by hand: 48 pages, 4423 internal links resolve; the docs_links, design_tokens and wire_rosters gates 25/25.
  • After rebasing onto 2d6d505 (one import-line conflict in IngestScreen.tsx): whole-tree ruff check and pnpm -r lint clean; ui-core screens 749 passed; tests/cli tests/mcp tests/architecture tests/server 1802 passed; the gallery specs and the real-server cycle re-run green.
  • New coverage: 13 unit tests for the three controls including each second-step refusal and the three states of the run card; 18 backend tests for the flags and parameters including the schema-less refusal (batch stays draft, exit non-zero, step named) and the idempotent zero-promoted outcome; a gallery e2e scenario pressing Complete and promote (job, batch, promotion, in order); cycle assertions that the run card's filled control is the composed step over a project with a schema, that a draft row offers it secondary, and that the header withholds it while frames are outstanding.

A baseline flake, not this branch's

The first full check.sh pass failed tests/scripts/test_cooldown_lockfile.py::test_an_interrupted_first_pass_changes_nothing and ::test_an_interruption_during_the_snapshot_changes_nothing with assert 0 == 130, the same two that failed the first pass on #794's branch. Nothing here touches scripts/cooldown.sh or tests/scripts/. Both pass in isolation on main and on the branch, the file passes under the configured parallelism repeatedly, and the python step re-run as check.sh runs it answers 4497 passed, 33 skipped. They time a signal against a live uv add and lose the race on a cold first run.

Also fixed

Nothing beyond the scope above.

@JArmandoAnaya
JArmandoAnaya force-pushed the feat/composed-transitions branch from c6fd667 to a478d5b Compare August 26, 2026 12:53
Each flag is the next lifecycle command made in the same run: approve then
start, complete then promote, and for ingest approve as one job then start.
No transition is added; start needs only approved and promote only completed,
so the second call is legal whenever the first succeeded. Each step commits on
its own, and a refused second step prints the first step's line, the refusal
as the single-step command prints it, and a line naming the step that refused
and the state the batch is in.

Plain output reports both outcomes and keeps the batch id alone on stdout.
With --json, approve --start and ingest --start print the started batch;
complete --promote prints {"batch": ..., "promoted": ...}, the closed batch
beside the page of assets that entered the dataset.
approve_batch(start=True) is start_batch in the same call and
complete_batch(promote=True) is promote_batch in the same call. Each answer
reports what it did: started says whether the batch was opened, promoted
counts the assets that entered the dataset, so a move is never an invisible
side effect. Approval and completion each commit before the next step is
attempted, and a refusal leaves the batch where the first step put it.

The tool reference is regenerated from the served listing.
The CLI reference, the MCP guide, the batches page and the tutorial name
approve --start, complete --promote and ingest --start, and the start and
promote parameters of the MCP tools. The tutorial opens a batch with the
one-command form and keeps the two-command walk as the explicit variant.
… each

Two lifecycle steps behind one control, offered on the first step's
declaration: Approve and start on a draft row of the batch table and on the
ingest run card once a run settles, where it is the card's filled control and
Open batch steps down; Complete and promote in the gallery header beside
Complete, withheld the same way while frames are outstanding. Each sends the
two requests in order and stops where the kernel stops it, showing the first
step's outcome as a line and a second-step refusal beneath it.

Approve and start reads the project's schema itself, since approval pins one
and the batch's declaration cannot say whether there is one to pin; without a
schema the row keeps its controls and the run card says what approving needs
and where to get it. A control keeps its own record of having been pressed,
so the line survives the batch's declaration moving on.
@JArmandoAnaya
JArmandoAnaya force-pushed the feat/composed-transitions branch from a478d5b to ad1d5aa Compare August 26, 2026 12:59
@JArmandoAnaya
JArmandoAnaya merged commit fd02b75 into main Aug 26, 2026
15 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the feat/composed-transitions branch August 26, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Composed transitions: "Approve and start" and "Complete and promote" on UI, CLI and MCP

1 participant