---
name: jwb-cli-contract
description: Use when 设计、新增或审查 jobws 命令行命令时，需要遵守本工作台既有的 CLI 契约（退出码三态、两段式写入、stdout/stderr 纪律、默认行为兼容）。English triggers：CLI design, command contract, clig.dev, exit codes, two-phase write, breaking change.
compatibility: jobws 指仓库内的 python tools/jobws.py（在仓库根运行）；面向「开发 / 扩展本仓库」的场景；Python 3.12+。
license: MIT
metadata:
  version: 26.9.0
---

# jobws CLI 契约（新增命令时照此办）

外部参考：clig.dev 的原则。本文件是**本仓库已落地的口径**——与代码及其测试冲突时，
以代码为准，并回来更新本文件。

## 退出码（三态，不可挪用）

| 码 | 含义 |
|---|---|
| 0 | 成功（只读命令即使结果为空也返回 0） |
| 1 | 业务失败（校验不过、令牌不可用、冲突……） |
| 2 | 用法或配置错误（参数不对、工作区不可用、旧入口迁移提示） |

## 写入必须是两段式（硬闸门）

- 写类命令默认直接执行，但必须提供 `--preview`：只校验、只给差异表与一次性令牌；
- 落盘统一走 `jobws apply <令牌>`——**不**在原命令里加 `--yes` 之类跳过确认的口；
- 令牌：一次性（apply 先取走再执行）、默认 10 分钟、绑定工作区与载荷指纹；
- 预览阶段不碰工作区一个字节（测试按文件字节断言）；
- 新增写入口时：在协议层注册表登记「操作名 → 落盘函数」，并同步 CLI / MCP /
  网页端三面——只改一面等于三面口径漂移。

## stdout / stderr 纪律

- stdout 只放结果（表格、令牌、记录 id）；诊断与进度走 stderr；
- **stdio MCP 场景下 stdout 是协议通道**——任何调试 print 都可能破坏会话；
- 机器可读的关键行（如令牌）保持固定格式，人可读提示与它分行。

## 默认行为与兼容

- 新增能力用**新增分支**承载（如 `--preview`）；旧路径默认行为不变——这是本仓库
  对「非破坏性」的默认防线；改名级别的大迁移单独开版本讲清迁移步骤；
- 参数名、子命令、退出码一经发布即契约；要改按破坏性流程走（Release 给迁移步骤）。

## 帮助与发现

- `jobws --help` 列全部命令；`jobws <cmd> --help` 显示子命令与参数——新增命令后
  两处都要对（入口里 `add_help=False` 的转发模式已固化）；
- 旧脚本被直接运行时必须给迁移提示（exit 2），不静默退出、也不留旧别名。

## 常用约定

- `--workspace` 在多个命令里是**顶层选项**：写在子命令之后会被判 unrecognized；
- 参数沿用既有风格（长选项、连字符分隔）；一次任务只做一件事；
- 错误信息写「怎么办」而不只写「哪里错」，涉及的命令要可直接复制执行。

## 常见错误

| 错误 | 后果 | 正确做法 |
|---|---|---|
| 写命令没有预览入口 | 用户对自己的数据失去确认权 | 加 `--preview`，走令牌 |
| 从 stdout 打调试日志 | stdio MCP 会话被污染 | 日志走 stderr |
| 改了默认输出格式 | 既有脚本解析失败 | 格式变更按破坏性流程 |
| 只改 CLI 面漏掉 MCP / 网页端 | 三面口径漂移 | 一处登记、三面同步 |
| 新增命令后忘更新帮助 | 用户找不到入口 | 跑一次 `--help` 对账 |
