DSH / Atlas
2026-08-08implementedfeaturearchived 2026-08-10

`dsh run` owns one-shot headless execution

`dsh run` 负责一次性 headless 执行

Generic profile boot and one-shot task execution have different lifecycle contracts. A root grammar that accepts optional task text makes one argv shape mean either a long-lived process or a terminating task according to a plugin row discovered only after composition. It also exposes a profile implementation detail as the primary user command and gives custom profiles no explicit one-shot entry. The `run` verb must h

English

Problem

Generic profile boot and one-shot task execution have different lifecycle contracts. A root grammar that accepts optional task text makes one argv shape mean either a long-lived process or a terminating task according to a plugin row discovered only after composition. It also exposes a profile implementation detail as the primary user command and gives custom profiles no explicit one-shot entry.

The run verb must have one top-level meaning. Sharing it with application-file execution or inferring its meaning from positional shape creates the same ambiguity.

Decision

One-shot execution owns this grammar:

dsh run [--profile <name>] [--patch <path>...] <task...>

--profile defaults to headless and supports custom one-shot compositions. --patch is repeatable and occupies the normal overlay layer. Commander joins the variadic task arguments with spaces and rejects a missing or blank task before boot.

RunInvocation is a distinct DshInvocation member. The generic profile invocation carries no task state and accepts no positional arguments. Both dispatch paths use runProfile: profile boot omits task, while run supplies it. A one-shot profile without headless-runner fails through the composed-row check, and profile boot containing that row without a task points to dsh run --profile <name> "<task>".

The profile plugin bundle decision owns composition. Headless is a direct core entry point owns the execution contract: one fresh persisted Session, final assistant text on stdout, completed/non-completed exit mapping, empty stderr on success, no listening port, and bounded signal shutdown after Agent quiescence and Session flush.

The run verb belongs only to one-shot task execution. Application-file launch requires a distinct command name.

Alternatives considered

AlternativeContract mismatch
Put task text on root profile bootLifecycle meaning depends on a plugin row discovered after parsing.
Accept root aliases such as dsh -pThe pre-release grammar acquires compatibility branches with no current command ownership.
Require --profile headlessThe shipped one-shot surface loses its shortest canonical spelling.
Use dsh run for application filesOne top-level verb has two meanings and the primary task command becomes indirect.
Add a shallow apps/cli/src/run.ts forwarderCommand ownership splits without hiding any complexity.

Consequences

Help, documentation, parser tests, built-bin acceptance, PTY shutdown coverage, and the assembled keyless snapshot use dsh run. Custom one-shot profiles use --profile; long-lived profile boot and config dumps keep the root profile grammar. Application-file execution is a separate command concern.

中文

问题

通用 profile 启动与一次性任务执行具有不同的生命周期约定。若根语法接受可选任务文本,同一种 argv 形态会表示常驻进程或终止式任务,具体含义取决于组合完成后才发现的插件配置行。它还会把 profile 实现细节暴露成主要用户命令,并使自定义 profile 缺少明确的一次性入口。

run 动词必须只有一种顶层含义。与应用文件执行共用该动词,或根据位置参数形态推断含义,都会产生相同的歧义。

决策

一次性执行采用以下语法:

dsh run [--profile <name>] [--patch <path>...] <task...>

--profile 默认为 headless,并支持自定义一次性组合。--patch 可重复使用,并占据正常的 overlay 层。Commander 用空格拼接可变数量的任务参数,并在启动前拒绝缺失或空白任务。

RunInvocation 是单独的 DshInvocation 成员。通用 profile 调用不携带任务状态,也不接受位置参数。两条分派路径都使用 runProfile:profile 启动省略 task,而 run 提供该字段。缺少 headless-runner 的一次性 profile 会触发组合行检查;如果启动的 profile 包含该行却未提供任务,错误会指向 dsh run --profile <name> "<task>"

profile 插件组合包决策负责组合。Headless 是直接 core 入口负责执行约定:一个新的持久化会话、stdout 上的最终 assistant 文本、completed/非 completed 的退出状态映射、成功时为空的 stderr、无监听端口,以及 Agent 完全停稳且会话 flush 后的有界信号关闭。

run 动词只负责一次性任务执行。应用文件启动需要不同的命令名。

考虑过的替代方案

替代方案约定不匹配之处
把任务文本放在根 profile 启动命令上生命周期含义依赖解析后才发现的插件配置行。
接受 dsh -p 等根命令别名预发布语法获得不属于任何当前命令的兼容分支。
要求指定 --profile headless随附的一次性 surface 失去最短的规范写法。
dsh run 用于应用文件一个顶层动词具有两种含义,主要任务命令也变得间接。
添加仅转发的 apps/cli/src/run.ts命令归属被拆分,却没有隐藏任何复杂度。

后果

帮助信息、文档、解析器测试、构建后二进制验收、PTY 关闭覆盖和组装应用的无密钥快照均使用 dsh run。自定义一次性 profile 使用 --profile;常驻 profile 启动与配置 dump 使用根 profile 语法。应用文件执行是独立的命令关注点。