Skip to content

Command Model

Z-M-Huang edited this page May 1, 2026 · 4 revisions

Command Model

contractVersion: 1.0.1

How commands dispatch. UI-agnostic. Acts on the main session only. Orthogonal to the attached SM.

The Command contract (shape, cardinality, registration) lives in contracts/Commands. This page is the dispatch model.


What a command is

A command is a named, parameterized action issued by the user (or by another extension through the Host API) and handled by a registered handler.

Examples that ship bundled (see Bundled commands): /model, /provider, /params, /save, /resume, /reload, /tools.

Commands are issued through any UI — TUI, web, scripts, CI invocation. The shape of the command does not depend on which UI raised it.


Dispatch

sequenceDiagram
    autonumber
    actor User
    participant UI
    participant Core as Command bus
    participant Handler
    participant Bus as Event bus

    User->>UI: types /model gpt-5
    UI->>Core: "dispatch('model', ['gpt-5'])"
    Core->>Handler: invoke(args, sessionAccessor)
    Handler-->>Core: result
    Core->>Bus: CommandDispatched + CommandResulted
    Core-->>UI: result
    UI->>User: render
Loading

Core resolves the command name through the command registry, invokes the handler with a Host API context, and publishes CommandDispatched and CommandResulted events.

If the command returns a result, the UI renders it. If it triggers an interaction (for example, a disambiguation Select), the request fans out through active interactors via the Interaction Protocol.


Commands act on the main session

A command sees the main session — the session the user is currently in. It does not see another session; it does not see an SM's private state; it does not see another extension's state slot directly (it uses the Host API, which mediates).

Practical consequences:

  • A command that mutates session state acquires a session-accessor lease. Leases serialize mutations so two concurrent commands cannot corrupt state.
  • A command does not trigger a turn. Turns run the message loop; commands do not. A command that needs an LLM response does so by invoking a dedicated command-facing API — it does not hijack the turn.
  • A command can run concurrently with a turn. It must not block the event loop. Long-running commands should yield via the Host API cancellation token.

Commands and SM orthogonality

This is an invariant.

  • Commands do not gate the SM. Issuing a command does not transition the SM, change its approval posture, or force a workflow fork.
  • The SM does not gate commands. The SM cannot deny /model or /save because the SM does not decide what the user can do outside the turn.

An SM that wants to constrain commands does so by cooperating with the UI extension (e.g., hiding commands in its theme) — not by vetoing through core.

Rationale: commands are user escape hatches. If the SM could lock the user out of /save or /resume, stud-cli would be an unusable cage. The SM owns the turn; the user owns the session.

See Extensibility Boundary and the locked decision in the plan's Architectural decisions table.


Security posture

Commands do not bypass the security stack.

  • A command that runs a tool passes through the normal Tool Approvals path: SM → mode gate → interactor.
  • A command that reads env values uses the env provider — it does not have bulk-read access. See LLM Context Isolation.
  • A command that switches provider or model passes through the Capability Negotiation fail-fast. /model foo rejects if foo lacks a capability the current session depends on.
  • Destructive commands (e.g., /forget, if added) request a Confirm interaction unless an SM is attached.

Commands are themselves extensions. They load through the normal validation pipeline with per-command severity defaulting to warn (a missing non-critical command degrades the session; it does not kill it). Critical commands may opt into hard-fail; this is documented per-command in contracts/Commands.


Identity and shadowing

Commands have flat names. The project scope can override or disable a command with the same name as a global or bundled command. Name collisions across scopes resolve by Configuration Scopes rules — later layer wins.

Shadowing is the user's responsibility. The /commands inspection command (if bundled) reveals origin. See also the tool-shadowing risk for the analogous tool case.


Events

Commands emit:

  • CommandDispatched{name, args, correlationId, origin} before invocation.
  • CommandResulted{name, outcome, correlationId} after.

These rides the event bus as projection. Interested UIs re-render; loggers log; audit records the invocation. Nothing controls flow by subscribing to these events.

Mutation events

Some commands emit additional kind-specific events to advertise their effect:

  • /params emits ParamsChanged{paramPath, sourceLayer, redactedDelta, correlationId} after the override lands. Payload is a redacted delta (path + shape-marker), never a raw value. Mutation profile: writes session-runtime params bag; does not write the manifest. See contracts/Commands § /params mutation profile.
  • /model, /provider, /sm attach, /sm detach emit their own kind-specific events documented on each command's bundled page.

Where commands show up in the wiki

Place What it covers
contracts/Commands The Command contract shape, cardinality, state slot, validation severity
Bundled commands Bundled commands: /model, /save, /resume, /reload, /provider, /tools, …
flows/Hot Model Switch /model mid-session with capability check
flows/Capability Mismatch Switch /model or /provider that fails fast

Changelog

1.0.0 — initial

  • UI-agnostic command dispatch model. Commands act on the main session only and are orthogonal to the attached SM. Mutation profile: each command declares its session-state effect; the dispatcher serializes commands FIFO per session.
  • Two events on every dispatch: CommandDispatched (pre-invocation) and CommandResulted (post-invocation). Both are projection events on the bus; control flow does not ride them.
  • Identity and shadowing: flat names; project-scope shadowing legal; collisions within a single layer are validation errors.
  • Security posture: commands do not bypass tool approvals, env-isolation, or capability negotiation; destructive commands request confirmation interactions when no SM is attached.

1.0.1 — /params and ParamsChanged event

  • Bundled-commands example list adds /params. Mutation profile documented on contracts/Commands § /params mutation profile: writes session-runtime params bag, does not write the manifest, refused while a stage is mid-Act.
  • New "Mutation events" subsection: documents ParamsChanged{paramPath, sourceLayer, redactedDelta, correlationId} emitted by /params and --param invocations. Payload is a redacted delta — never raw values — per the Audit Trail redaction pipeline.
  • No changes to dispatch ordering, security posture, or shadowing semantics.

Introduction

Reading

Core runtime

Contracts

Category contracts

Context

Security

Runtime behavior

Operations

Providers (bundled)

Integrations

Reference extensions

Tools

UI

Session Stores

Loggers

Providers

Hooks

Context Providers

Commands

Case studies

Flows

Maintainers

Clone this wiki locally