A person should be able to inspect a capability, choose its placement and run it later. If registration begins Python work or external input, that sequence breaks. I separated preference operations from task-start operations in the actual functions and made their receipts describe the particular event that occurred.

Separate creation from execution in the receipt

TaskPluginService.create either connects an existing programId or creates a definition from a restricted recipe before registration. Supplying both is rejected as ambiguous. This is not a registry accepting arbitrary shell commands, and its response explicitly includes executed=False. A draft_unverified definition remains an unverified draft even when registration succeeds.

Automation callers need that distinction too. Answering that the business task finished after receiving a creation receipt skips current input and outcome checks. I made the response readable as a statement about what was prepared. Exact wording at this boundary lets the next execution request begin from known facts instead of inheriting a fictional success.

Opening prepares the launcher

The pluginAction open branch verifies a current ordinary profile and refreshes the list. It checks registration and enabled state, stores the selected identity and shows the launcher. There is no runner.start in that branch. Other preference operations such as pinning or enabling likewise do not start the program.

A nearby run button does not make these equivalent actions. Opening creates an opportunity to review required inputs and usage before committing work. Management APIs can preserve the same distinction between preparation and start receipts. The independent example returns prepared=True with executed=False so that this difference remains visible in machine-readable output, rather than existing only in explanatory copy.

Bind the current tab again immediately before starting

pluginRun first rejects another start already being prepared. It captures the current profile, workspace and tab and compares all three with supplied values. Private mode, another active program and a busy notebook are rejected. After asynchronously loading the definition, the branch repeats its check, so a changed target during the read does not pass on the strength of an earlier observation.

Registration context may no longer be execution context. Users naturally switch tabs and projects while a launcher is open. An old selection must not become the current target merely because a button remains available. Making execution convenient should not require trusting an indefinitely stale choice. The boundary belongs at the actual start, where the relevant state can be compared again.

Matching input count is not enough; keys must match

Inputs are copied from the supplied dictionary. The branch compares its key count with recipe parameters and checks that every required key exists. A few supported current-page capabilities derive a current URL or owner only after validating the procedure kind. Attaching a current-page flag to an arbitrary program is not a universal bypass.

This does not mean every lower-level helper forbids merging defaults. Some underlying preparation paths do merge them. The claim here concerns the UI pluginRun entry requiring current inputs. Stating the particular layer matters when another developer adapts the design: a check at one entrance is not automatically a contract enforced by every function below it.

Disabling does not start work, but can affect active work

Disable and remove never initiate a new run. They do find a matching active program by profile and program identity and request cancellation, while clearing its selected launcher. I therefore do not claim that settings operations have no effect on execution at all. Absence of a new start differs from preserving an already running task.

There is also an enabled-state check after runner.start for a disable that occurred while startup was pending. It sends a cancellation request; it does not prove reversal of every external effect. Actual outcome and journal reconciliation remain necessary after stopping. The synthetic module calls its field cancel_requested for exactly this reason, reserving completion language for a result it can actually observe.

Read tests and wording at the same boundary

Existing creation tests inspect executed=False for new definitions and reused ones, while malformed inputs must leave no registration. Preference tests reject opening a disabled entry. I read these tests and compared the actual branches containing runner calls; this is not a report of starting fresh work in the original app.

The linked module tests registration and opening without execution and returns a cancellation intent for disable/remove. It implements neither real authentication nor runner interruption. A manager UI and an agent API both become clearer when saved, prepared, started and completed describe separate events. The benefit is an understandable account of what just happened as the capability library grows.