@fateforge/xpedition-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/AGENT.md +59 -0
- package/.agent/AGENT_zh.md +59 -0
- package/.agent/CLI-SPEC.md +1073 -0
- package/.agent/CLI-SPEC_zh.md +891 -0
- package/.agent/SEC-SPEC.md +158 -0
- package/.agent/SEC-SPEC_zh.md +132 -0
- package/.agent/SKILL-SPEC.md +266 -0
- package/.agent/SKILL-SPEC_zh.md +221 -0
- package/.agent/SPEC_VERSION +1 -0
- package/AGENTS.md +34 -0
- package/AGENTS_zh.md +33 -0
- package/CHANGELOG.md +795 -0
- package/CODE_OF_CONDUCT.md +35 -0
- package/CODE_OF_CONDUCT_zh.md +35 -0
- package/CONTRIBUTING.md +50 -0
- package/CONTRIBUTING_zh.md +42 -0
- package/LICENSE +21 -0
- package/NOTICE.md +16 -0
- package/NOTICE_zh.md +13 -0
- package/README.md +200 -0
- package/README_zh.md +178 -0
- package/SECURITY.md +108 -0
- package/SECURITY_zh.md +83 -0
- package/docs/AGENT_HARDENING_EVIDENCE.md +102 -0
- package/docs/AGENT_READS.md +74 -0
- package/docs/AGENT_READS_METRICS.json +216 -0
- package/docs/AGENT_READS_VALIDATION.json +13 -0
- package/docs/API_INVENTORY_BINDING_VALIDATION.json +16 -0
- package/docs/API_INVENTORY_DESIGN.md +90 -0
- package/docs/API_INVENTORY_REVIEW.md +59 -0
- package/docs/API_INVENTORY_VALIDATION.json +29 -0
- package/docs/API_INVENTORY_WINDOWS_VALIDATION.json +29 -0
- package/docs/COMPATIBILITY.md +499 -0
- package/docs/CONFIRMATION_CONCURRENCY_VALIDATION.json +33 -0
- package/docs/DIAGNOSTIC_BOUNDARIES.md +33 -0
- package/docs/DIAGNOSTIC_BOUNDARIES_VALIDATION.json +12 -0
- package/docs/E2E.md +445 -0
- package/docs/EVALS.md +134 -0
- package/docs/MCP.md +20 -0
- package/docs/NATIVE_ADAPTER.md +141 -0
- package/docs/OPEN_SOURCE_CHECKLIST.md +61 -0
- package/docs/OPEN_SOURCE_CHECKLIST_zh.md +61 -0
- package/docs/PIN_WORKFLOW_VALIDATION.json +28 -0
- package/docs/PLACEMENT_TASKS.md +99 -0
- package/docs/PLACEMENT_TASKS_VALIDATION.json +36 -0
- package/docs/REFERENCE_ADOPTION.md +67 -0
- package/package.json +48 -0
- package/scripts/run.js +46 -0
- package/skills/xpedition-cli/SKILL.md +300 -0
- package/skills/xpedition-cli/reference/agent-hardening.md +58 -0
- package/skills/xpedition-cli/reference/api-inventory.md +58 -0
- package/skills/xpedition-cli/reference/confirmation-safety.md +55 -0
- package/skills/xpedition-cli/test-prompts.json +62 -0
- package/skills/xpedition-pcb/SKILL.md +244 -0
- package/skills/xpedition-pcb/reference/fabrication.md +26 -0
- package/skills/xpedition-pcb/reference/hand-routing.md +33 -0
- package/skills/xpedition-pcb/reference/pcb-conventions.md +162 -0
- package/skills/xpedition-pcb/reference/placement-tasks.md +28 -0
- package/skills/xpedition-pcb/test-prompts.json +62 -0
- package/skills/xpedition-schematic/SKILL.md +244 -0
- package/skills/xpedition-schematic/reference/pin-assignment.md +61 -0
- package/skills/xpedition-schematic/reference/schematic-conventions.md +306 -0
- package/skills/xpedition-schematic/reference/schematic-design-format.md +219 -0
- package/skills/xpedition-schematic/test-prompts.json +52 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
# 面向 Agent 的 CLI 工具设计规范
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
本文定义本仓库 CLI 在被 AI Agent 调用时必须遵守的机器契约。目标是让 Agent 能稳定调用、可靠解析、可恢复重试,并避免任何非交互场景下的阻塞或误写。
|
|
5
|
+
|
|
6
|
+
## 1. 核心铁律
|
|
7
|
+
|
|
8
|
+
1. stdout 是契约:默认输出单个合法 JSON 文档,禁止混入日志、进度、提示、颜色控制符。
|
|
9
|
+
2. stderr 是旁路:进度、警告、调试、错误说明全部写 stderr。
|
|
10
|
+
3. 机器优先:默认 `--format json`;`text` 仅供人读;`raw` 仅用于裸字节、日志、diff 等原样透传。
|
|
11
|
+
4. 非交互安全:写操作不得等待键盘输入,必须使用 `--dry-run` + `--confirm <token>`。
|
|
12
|
+
5. 确定性:相同输入产生相同结构输出;字段名、字段顺序、schema 版本保持稳定。
|
|
13
|
+
6. 最小惊扰:查询不改变状态;写操作在缺少有效 confirm token 时必须失败而不是继续执行。
|
|
14
|
+
7. 可恢复:错误码、exit code、`retryable` 必须足够稳定,让 Agent 能决定是否重试、回退或请求用户介入。
|
|
15
|
+
|
|
16
|
+
## 2. 全局参数
|
|
17
|
+
|
|
18
|
+
| 参数 | 说明 |
|
|
19
|
+
|--------------------------|--------------------------------|
|
|
20
|
+
| `--format json/text/raw` | 输出格式,默认 `json` |
|
|
21
|
+
| `--json` | `--format json` 的兼容别名,不推荐新调用使用 |
|
|
22
|
+
| `--fields <a,b,c>` | 仅返回指定字段,降低 token(查询命令) |
|
|
23
|
+
| `--compact` | 紧凑 JSON 输出,去除冗余空白(查询命令) |
|
|
24
|
+
| `--dry-run` | 模拟写操作,返回变更预览与 `confirm_token` |
|
|
25
|
+
| `--confirm <token>` | 携带 dry-run 返回的 token 真正执行写操作 |
|
|
26
|
+
| `--quiet` | 抑制 stderr 上的进度/提示,不抑制错误 |
|
|
27
|
+
|
|
28
|
+
`update` 是**单命令,不是带确认门禁的写操作**(见 §14):裸 `update` 一次调用完成整个自更新。它可按工具增加 `--target-version`、`--channel` 等参数,并保留 `--check`、`--dry-run` 作为**可选只读**标志,但**不接受 `--confirm <token>`**——自更新豁免 §7 写门禁。
|
|
29
|
+
|
|
30
|
+
格式职责:
|
|
31
|
+
|
|
32
|
+
- `json`:结构化机器输出,默认格式,也是唯一推荐给 Agent 的格式。
|
|
33
|
+
- `text`:人类可读,可以变化,禁止程序解析。
|
|
34
|
+
- `raw`:未包装 bytes / log / diff,原样透传,不使用 JSON envelope。
|
|
35
|
+
|
|
36
|
+
## 3. 统一输出 Envelope
|
|
37
|
+
|
|
38
|
+
成功与失败使用同形结构。Agent 只需要先判断 `ok`。
|
|
39
|
+
|
|
40
|
+
成功:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"ok": true,
|
|
45
|
+
"schema_version": "1.0",
|
|
46
|
+
"data": {},
|
|
47
|
+
"meta": {
|
|
48
|
+
"duration_ms": 0
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
失败:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"ok": false,
|
|
58
|
+
"schema_version": "1.0",
|
|
59
|
+
"error": {
|
|
60
|
+
"code": "E_NOT_FOUND",
|
|
61
|
+
"message": "human readable message",
|
|
62
|
+
"details": {},
|
|
63
|
+
"retryable": false
|
|
64
|
+
},
|
|
65
|
+
"meta": {
|
|
66
|
+
"duration_ms": 0
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
约定:
|
|
72
|
+
|
|
73
|
+
- 所有 JSON 响应必须包含 `ok` 与 `schema_version`。
|
|
74
|
+
- `data` 始终是命令的业务载荷;不要把业务字段提升到 envelope 顶层。
|
|
75
|
+
- `error.code` 使用稳定的语义化枚举,统一以 `E_` 开头。
|
|
76
|
+
- `error.message` 给人读,Agent 不应解析。
|
|
77
|
+
- `error.details` 放结构化上下文,必须脱敏。
|
|
78
|
+
- `error.retryable` 决定 Agent 是否可以自动退避重试。
|
|
79
|
+
- `meta.duration_ms` 记录命令执行耗时。`meta` 在每个响应(成功与失败)里都要输出,
|
|
80
|
+
不要标 `omitempty`——`duration_ms: 0` 是合法值,agent 应当总能读到。
|
|
81
|
+
- `meta.notices[]`(可选)MAY 携带环境型运营通知——当前即「有可用更新」通知——
|
|
82
|
+
**只从本地缓存读取**,绝不发起网络。每条通知带 `severity`(`info` | `warning`)。
|
|
83
|
+
无内容可报时省略该字段。详见 §14。
|
|
84
|
+
- 破坏性 schema 变更必须升级 `schema_version` 主版本。
|
|
85
|
+
|
|
86
|
+
### 3.1 机器可读契约(`contract.json`)—— 单一真源、强制对齐
|
|
87
|
+
|
|
88
|
+
§3/§6/§11 的散文有一个机器可读的孪生体:**`contract/contract.json`**,**只**在本模板仓维护,是全体工具共享字段的单一真源。它编码了信封键集、`E_*` ↔ 退出码 ↔ `retryable` 表、自描述必含键、命名规约、分页/批量形状以及 `_untrusted` 键。
|
|
89
|
+
|
|
90
|
+
- **单源、副本、零漂移。** 工具不手写这些。它把 `contract.json`(与 `.agent` 规范)按 `.agent/SPEC_VERSION` 里的 tag 钉住并 vendor 一份副本,用 `scripts/gen-contract.js` 从中生成各语言模块(`contract_gen.{go,py}`);一个失败关闭的 CI 守卫(`scripts/check-spec.js`)保证副本与 `template@<pin>` 逐字节一致、且生成模块与 `contract.json` 同步。改契约只发生在模板;工具升 pin 并跑 `scripts/sync-spec.js`。
|
|
91
|
+
- **核心冻结;功能扩展,绝不重定义。** `error_codes.core` 在每个工具里完全一致(正是这点让退出码/retryable 行为可移植)。工具独有的错误码放进 `contract-ext.json` 并受校验:ext 码必须是 `E_*`、必须声明 `{exit, retryable}`、不得覆盖 core 码,退出码 `9` 保留给 human-action 码(`E_HUMAN_REQUIRED`、`E_2FA_REQUIRED`)。这就是契约既统一、又支持独有字段的方式。
|
|
92
|
+
- **强制力分级。** 信封键、错误码表、`meta` 键、`schema_version` 值 **精确** 匹配。自描述块(`reference` / `context` / `doctor` / `changelog` / `update`)必须含 **必需** 的 canonical 键,但可追加工具特定键——所以独有功能命令绝不被卡,只锁共享面。每个工具有一个运行时一致性测试,把真实命令输出对照 `contract.json`。
|
|
93
|
+
|
|
94
|
+
## 4. stdout / stderr 规则
|
|
95
|
+
|
|
96
|
+
- stdout 在 `json` 模式下只能出现一个 JSON 文档,或在明确流式命令中输出 NDJSON。
|
|
97
|
+
- stderr 可输出进度、警告、诊断。
|
|
98
|
+
- `json` 模式下出错时,失败 envelope 就是 stdout 上那个唯一的 JSON 文档——Agent 永远解析 stdout 并检查 `ok`,不从 stderr 抓取;stderr 可以补充人类可读的错误说明。
|
|
99
|
+
- `--quiet` 只能抑制 stderr 上的非错误信息。
|
|
100
|
+
- 不允许在 JSON stdout 前后打印 banner、提示语、进度条或颜色码。
|
|
101
|
+
- stdout / stderr 一律 **UTF-8 编码,不带 BOM**,换行用 `\n`,确保跨平台(尤其 Windows)Agent 可稳定解析。
|
|
102
|
+
|
|
103
|
+
## 5. 流式输出(NDJSON)
|
|
104
|
+
|
|
105
|
+
大输出、日志流、订阅流、批量逐项结果使用 NDJSON。每行必须是独立合法 JSON,便于流式消费、降内存、可中断:
|
|
106
|
+
|
|
107
|
+
```jsonl
|
|
108
|
+
{"ok":true,"schema_version":"1.0","type":"item","data":{}}
|
|
109
|
+
{"ok":true,"schema_version":"1.0","type":"item","data":{}}
|
|
110
|
+
{"ok":true,"schema_version":"1.0","type":"summary","data":{"count":2}}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
约定:
|
|
114
|
+
|
|
115
|
+
- 普通查询默认使用单 JSON envelope。
|
|
116
|
+
- 只有命令语义明确是 log / stream / subscribe / 批量流式输出时才使用 NDJSON。
|
|
117
|
+
- NDJSON 行必须包含 `ok`、`schema_version`、`type`。
|
|
118
|
+
- 结束行建议使用 `type: "summary"`。
|
|
119
|
+
- 真正的二进制或纯文本透传走 `--format raw`,不要包成巨型单 JSON。
|
|
120
|
+
|
|
121
|
+
## 6. Exit Code 语义表
|
|
122
|
+
|
|
123
|
+
| Code | 含义 | Agent 行为 |
|
|
124
|
+
|------|--------------------|------------------------|
|
|
125
|
+
| 0 | 成功 | 继续 |
|
|
126
|
+
| 1 | 通用错误 | 读取 error envelope 判断 |
|
|
127
|
+
| 2 | 参数/用法错误 | 不重试,修正参数 |
|
|
128
|
+
| 3 | 资源不存在 | 不重试 |
|
|
129
|
+
| 4 | 权限/认证/配置失败 | 不重试,提示凭证或权限 |
|
|
130
|
+
| 5 | 需确认但缺少 token | 走 dry-run 获取 token 后重试 |
|
|
131
|
+
| 6 | 前置条件冲突或 token 失效 | 重新读取状态后重试 |
|
|
132
|
+
| 7 | 可重试瞬时错误(网络/限流/服务端) | 退避后重试 |
|
|
133
|
+
| 8 | 超时 | 退避后重试 |
|
|
134
|
+
| 9 | 需人工介入(见 §16.3,可选) | 转述给用户,待其完成后跑 `resume` |
|
|
135
|
+
|
|
136
|
+
错误码与 exit code 必须一致:
|
|
137
|
+
|
|
138
|
+
- `E_USAGE` / `E_VALIDATION` -> 2
|
|
139
|
+
- `E_NOT_FOUND` -> 3
|
|
140
|
+
- `E_AUTH` / `E_FORBIDDEN` / `E_CONFIG` -> 4
|
|
141
|
+
- `E_CONFIRMATION_REQUIRED` -> 5
|
|
142
|
+
- `E_CONFLICT` -> 6
|
|
143
|
+
- `E_NETWORK` / `E_RATE_LIMITED` / `E_SERVER` -> 7
|
|
144
|
+
- `E_TIMEOUT` -> 8
|
|
145
|
+
- `E_INTEGRITY` -> 1(发布完整性失败:签名缺失/无效或 checksum 不符;**非重试**,见 §14)
|
|
146
|
+
- `E_IO` -> 1(本地文件系统失败:磁盘满、文件被占用、写入不全;非重试,需修环境;见 §14 update 替换阶段)
|
|
147
|
+
- `E_INTERRUPTED` -> 130(被信号/用户取消,SIGINT = 128+2;可重试——分阶段执行不会留下半截状态,见 §14)
|
|
148
|
+
- `E_HUMAN_REQUIRED` -> 9(可选,仅启用 §16.3 时)
|
|
149
|
+
|
|
150
|
+
当错误来自上游 HTTP 调用时,按状态码映射到错误码,让 agent 能从 `error.code` +
|
|
151
|
+
`retryable` 区分失败类型——不要把所有 4xx 都塌缩成 `E_NETWORK`:
|
|
152
|
+
|
|
153
|
+
- `401` -> `E_AUTH`
|
|
154
|
+
- `403` -> `E_FORBIDDEN`
|
|
155
|
+
- `404` -> `E_NOT_FOUND`
|
|
156
|
+
- `408` -> `E_TIMEOUT`(可重试)
|
|
157
|
+
- `409` -> `E_CONFLICT`
|
|
158
|
+
- `429` -> `E_RATE_LIMITED`(可重试)
|
|
159
|
+
- `5xx` -> `E_SERVER`(可重试)
|
|
160
|
+
- 连接拒绝 / DNS / 重置 -> `E_NETWORK`(可重试)
|
|
161
|
+
|
|
162
|
+
优先按上游的错误类型/状态码映射,不要靠匹配人类可读的 message 文本(子串匹配会把仅仅
|
|
163
|
+
包含「not found」等字样的消息误判)。把这套映射收敛到**一个**函数里,避免 output
|
|
164
|
+
层与 command 层的 status->code->exit 契约漂移。声明了但运行时不可达的错误码应标注为
|
|
165
|
+
保留,免得 agent 为不会发生的分支做规划。
|
|
166
|
+
|
|
167
|
+
## 7. 写操作流程(dry-run -> confirm)
|
|
168
|
+
|
|
169
|
+
写命令第一步必须支持 `--dry-run`,返回预览与 token:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"ok": true,
|
|
174
|
+
"schema_version": "1.0",
|
|
175
|
+
"data": {
|
|
176
|
+
"preview": {
|
|
177
|
+
"changes": [
|
|
178
|
+
{
|
|
179
|
+
"action": "delete",
|
|
180
|
+
"resource": "mail",
|
|
181
|
+
"id": "123",
|
|
182
|
+
"before": {},
|
|
183
|
+
"after": null
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
"confirm_token": "ct_9f2a...",
|
|
188
|
+
"expires_at": "2026-06-05T12:00:00Z"
|
|
189
|
+
},
|
|
190
|
+
"meta": {
|
|
191
|
+
"duration_ms": 0
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
第二步携带 token 执行:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
tool resource delete --id 123 --confirm ct_9f2a...
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
confirm token 约定:
|
|
203
|
+
|
|
204
|
+
- token 必须绑定操作内容哈希,包括命令路径、参数、目标资源 ID、调用账号、权限上下文。
|
|
205
|
+
- 哈希必须用机器本地密钥做 HMAC(如 `~/.<tool>/confirm.secret`,首次使用时生成,权限 `0600`),使 token 无法通过重算公开哈希凭空铸造——它只能来自同一台机器上一次真实的 `--dry-run`。
|
|
206
|
+
- 能获取资源版本时,也应绑定资源版本、etag、changekey 或 updated_at,防止状态漂移。
|
|
207
|
+
- token 必须有过期时间,`expires_at` 使用 ISO 8601 UTC。
|
|
208
|
+
- token 过期、操作参数变化、目标状态变化时,执行命令返回 `E_CONFLICT`,exit code 6。
|
|
209
|
+
- 缺少 token 时返回 `E_CONFIRMATION_REQUIRED`,exit code 5。
|
|
210
|
+
- dry-run 不得产生外部副作用,但可以读取状态用于构造 preview。
|
|
211
|
+
|
|
212
|
+
## 8. 查询、分页与字段选择
|
|
213
|
+
|
|
214
|
+
查询命令默认支持:
|
|
215
|
+
|
|
216
|
+
- `--fields <a,b,c>`:只返回指定字段,支持 dotted path 时需在 reference 中声明。
|
|
217
|
+
- `--compact`:压缩 JSON 空白。
|
|
218
|
+
- `--limit`:限制返回条数。
|
|
219
|
+
- `--cursor` 或 `--offset`:分页游标或偏移。
|
|
220
|
+
|
|
221
|
+
分页返回建议:
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"items": [],
|
|
226
|
+
"count": 0,
|
|
227
|
+
"next_cursor": null,
|
|
228
|
+
"has_more": false
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
对基于 offset 的上游,回显 `offset` 并返回显式的 `next_offset`(下一页要传的值,
|
|
233
|
+
仅在 `has_more` 为 true 时出现),让 agent 确定性翻页,而不必自己从 `offset + count`
|
|
234
|
+
推导:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"items": [],
|
|
239
|
+
"count": 0,
|
|
240
|
+
"offset": 0,
|
|
241
|
+
"next_offset": 20,
|
|
242
|
+
"has_more": true
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
当列表被静默截断(例如自动翻页上限)时,要输出 `truncated: true`,而不是返回一个看起来
|
|
247
|
+
完整、实则被截短的列表。
|
|
248
|
+
|
|
249
|
+
约定:
|
|
250
|
+
|
|
251
|
+
- 所有 ID 使用字符串,即使底层是数字。
|
|
252
|
+
- 所有时间使用 ISO 8601 UTC。
|
|
253
|
+
- 列表顺序必须稳定;默认排序规则应在 reference 中声明。
|
|
254
|
+
- 查询命令不得因为缺少可选过滤条件而进入交互询问。
|
|
255
|
+
|
|
256
|
+
### 8.1 优先服务端过滤,不做客户端假过滤
|
|
257
|
+
|
|
258
|
+
能下推到上游的过滤就不要在客户端对已取到的某一页做后过滤。分页之后再过滤会**静默少算**:看起来全,实则只反映取回的那一页。若上游在某个已知版本新增了该过滤能力,就把 flag 映射到它,并记录最低版本(reference + 兼容性文档),而不是自行模拟;万不得已要本地过滤,必须先取全量再过滤,并在输出里说明。
|
|
259
|
+
|
|
260
|
+
### 8.2 重子资源独立、结构化、可投影
|
|
261
|
+
|
|
262
|
+
大小无界的子资源(diff、日志、artifact、完整文件正文)是独立命令,绝不内联进列表。廉价、有界的摘要(数量、路径、stats)放在列表里;重负载只为 agent 选中的那一项按需获取。返回时要**结构化**(如 diff 拆成逐文件条目,而不是一坨不透明文本),让 `--fields` 能把它投影成清单(路径 + 行数)而不传输负载。这样 agent 的 token 成本是它自己的选择,而不是被动挨炸——无需专门的截断协议。
|
|
263
|
+
|
|
264
|
+
### 8.3 多作用域查询走批量契约扇出
|
|
265
|
+
|
|
266
|
+
当一次读取跨越多个容器(一个组下的项目、整个实例的项目)时,先把作用域解析成一个具体集合,再以客户端循环扇出(§15,B 类):一个对外命令、恰好一个作用域选择器、结果聚合且每条都标注其来源容器。扫描失败的容器要在结果里报出(如 `projectErrors[]` / `scope` / `projectsScanned`),绝不静默丢弃,且单个失败不得中断其余。跨容器聚合**有界元数据**是安全的;绝不跨整个集合聚合无界子资源(§8.2)。只有对某个主体才有意义的全实例作用域(某人的全部提交)必须绑定该主体,否则 fail-closed,使无界裸扫不可能发生。
|
|
267
|
+
|
|
268
|
+
## 9. 幂等性与并发安全
|
|
269
|
+
|
|
270
|
+
写命令应尽量支持幂等语义:
|
|
271
|
+
|
|
272
|
+
- 创建类命令建议支持 `--request-id` 或 `--idempotency-key`。上游若支持幂等头(如 GitLab 的
|
|
273
|
+
`Idempotency-Key`),应转发该头,并把 key 绑进 confirm scope,使 token 只对该 key 生效。
|
|
274
|
+
- 重试同一个 idempotency key 不应重复创建资源。
|
|
275
|
+
- 更新/删除类命令应在 dry-run 中记录目标资源版本。
|
|
276
|
+
- confirm 时发现版本变化必须返回 `E_CONFLICT`。
|
|
277
|
+
- **confirm token 单次消费。** token 一旦被接受执行写操作,就记录其指纹(如
|
|
278
|
+
`~/.<tool>/confirm-consumed.json`,按过期裁剪),任何重放都以 `E_CONFLICT` 拒绝(「token 已用过,
|
|
279
|
+
请重新 `--dry-run`」)。这给 agent 安全重试语义:确认后超时的写不能盲目重发——重放被拒,重新
|
|
280
|
+
`--dry-run` 会显示当前真实状态。对于无资源版本可绑的上游,这是通用的安全重试机制。要在写执行**之前**
|
|
281
|
+
标记消费(中途崩溃宁可保守拒绝重放,也不冒重复风险)。存储失败要优雅降级,绝不阻塞写。
|
|
282
|
+
- 批量写操作应返回逐项结果,不要因为单项失败隐藏其他项状态。
|
|
283
|
+
|
|
284
|
+
批量写结果建议:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"results": [
|
|
289
|
+
{
|
|
290
|
+
"id": "1",
|
|
291
|
+
"ok": true,
|
|
292
|
+
"action": "deleted"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"id": "2",
|
|
296
|
+
"ok": false,
|
|
297
|
+
"error": {
|
|
298
|
+
"code": "E_NOT_FOUND"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
"summary": {
|
|
303
|
+
"ok_count": 1,
|
|
304
|
+
"error_count": 1
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## 10. 敏感信息与审计
|
|
310
|
+
|
|
311
|
+
- password、token、secret、authorization header、cookie 不得出现在 stdout、stderr、error.details、audit log。
|
|
312
|
+
- dry-run preview 必须脱敏敏感字段。
|
|
313
|
+
- reference/context/doctor 不得泄露明文凭证。
|
|
314
|
+
- context 可以报告凭证是否存在,但只能用布尔值或脱敏摘要。
|
|
315
|
+
- audit log 应记录命令路径、脱敏参数、调用账号、时间、exit code、duration。
|
|
316
|
+
- `--quiet` 不应关闭审计。
|
|
317
|
+
|
|
318
|
+
## 11. 自描述命令(reference / context / doctor / changelog)
|
|
319
|
+
|
|
320
|
+
### reference
|
|
321
|
+
|
|
322
|
+
声明工具能力、命令、参数、输出 schema、错误码、权限等级,供 Agent 先理解能力。
|
|
323
|
+
|
|
324
|
+
每个命令的 `output_schema` 必须可被机器使用,不能是占位 stub。用一个字符串 label 指向顶层
|
|
325
|
+
`schemas` 目录里的条目:`{ "shape": "object"|"array", "fields": [...], "untrusted_fields": [...] }`,
|
|
326
|
+
其中字段清单从命令真实返回的数据(flatten 结构 / `*ToMap` 构造)枚举,`untrusted_fields` 列出
|
|
327
|
+
攻击者可控的键。每个命令还应带 `examples`:一条可直接运行的调用(写命令展示 `--dry-run` 再
|
|
328
|
+
`--confirm` 这一对,危险命令带 `--dangerous`)。应有一个 guard 测试断言每个叶子命令都解析到非空
|
|
329
|
+
schema 且至少有一条 example,使 `reference` 不会悄悄退化回 stub。
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
{
|
|
333
|
+
"ok": true,
|
|
334
|
+
"schema_version": "1.0",
|
|
335
|
+
"data": {
|
|
336
|
+
"tool": "tool-name",
|
|
337
|
+
"version": "1.0.0",
|
|
338
|
+
"release_readiness": {
|
|
339
|
+
"level": "beta",
|
|
340
|
+
"fcc_required": true,
|
|
341
|
+
"fcc_status": "verified",
|
|
342
|
+
"mock_upstream_required": true,
|
|
343
|
+
"mock_upstream_status": "verified",
|
|
344
|
+
"live_smoke_required_for_stable": true,
|
|
345
|
+
"live_smoke_status": "missing",
|
|
346
|
+
"reason": "Stable requires recorded live smoke/E2E evidence.",
|
|
347
|
+
"required_evidence": [
|
|
348
|
+
"functional_contract_coverage_100",
|
|
349
|
+
"mock_upstream_contract_tests",
|
|
350
|
+
"recorded_live_smoke_for_stable"
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
"commands": [
|
|
354
|
+
{
|
|
355
|
+
"path": "resource delete",
|
|
356
|
+
"type": "write",
|
|
357
|
+
"description": "Delete a resource",
|
|
358
|
+
"params": [
|
|
359
|
+
{
|
|
360
|
+
"name": "id",
|
|
361
|
+
"type": "string",
|
|
362
|
+
"required": true,
|
|
363
|
+
"multiple": false
|
|
364
|
+
}
|
|
365
|
+
],
|
|
366
|
+
"output_schema": "deleted_resource",
|
|
367
|
+
"examples": [
|
|
368
|
+
"<tool> resource delete <id> --dry-run --compact",
|
|
369
|
+
"<tool> resource delete <id> --confirm <confirm_token> --compact"
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
],
|
|
373
|
+
"schemas": {
|
|
374
|
+
"deleted_resource": {
|
|
375
|
+
"shape": "object",
|
|
376
|
+
"fields": ["id", "status"],
|
|
377
|
+
"untrusted_fields": []
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"exit_codes": {}
|
|
381
|
+
},
|
|
382
|
+
"meta": {
|
|
383
|
+
"duration_ms": 0
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`release_readiness` 是机器可读的发布门禁字段。每个 AI 原生 CLI 的
|
|
389
|
+
`reference` 都必须包含:
|
|
390
|
+
|
|
391
|
+
- `level`:`stable`、`beta` 或 `unpublishable`。
|
|
392
|
+
- `stable`:FCC 达到 100%;mock upstream / contract tests 覆盖外部行为;
|
|
393
|
+
且该 release candidate 至少有一次可追溯的真实环境 smoke/E2E 记录。
|
|
394
|
+
- `beta`:FCC 达到 100%,mock upstream / contract tests 完整,但缺少真实
|
|
395
|
+
smoke/E2E 记录,或项目明确声明暂不具备真实 E2E 条件。
|
|
396
|
+
- `unpublishable`:任一公开行为缺少命令级测试,或 mock upstream / contract
|
|
397
|
+
tests 只覆盖 happy path,未覆盖失败、鉴权、分页、空结果、限流等行为。
|
|
398
|
+
- `fcc_status`、`mock_upstream_status`、`live_smoke_status` 使用
|
|
399
|
+
`verified`、`missing`、`not_applicable` 或 `unknown`;`stable` 对必需项不得
|
|
400
|
+
使用 `missing` 或 `unknown`。
|
|
401
|
+
- `required_evidence[]` 列出 Agent 或发布脚本信任该等级前应检查的证据。
|
|
402
|
+
|
|
403
|
+
### context
|
|
404
|
+
|
|
405
|
+
报告当前运行环境、配置、目标、凭证状态。
|
|
406
|
+
|
|
407
|
+
```json
|
|
408
|
+
{
|
|
409
|
+
"ok": true,
|
|
410
|
+
"schema_version": "1.0",
|
|
411
|
+
"data": {
|
|
412
|
+
"env": "prod",
|
|
413
|
+
"account": "user@example.com",
|
|
414
|
+
"config": {},
|
|
415
|
+
"credentials": {
|
|
416
|
+
"configured": true
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
"meta": {
|
|
420
|
+
"duration_ms": 0
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### doctor
|
|
426
|
+
|
|
427
|
+
环境与风险体检,每项给出可执行修复建议。
|
|
428
|
+
|
|
429
|
+
```json
|
|
430
|
+
{
|
|
431
|
+
"ok": true,
|
|
432
|
+
"schema_version": "1.0",
|
|
433
|
+
"data": {
|
|
434
|
+
"checks": [
|
|
435
|
+
{
|
|
436
|
+
"check": "auth",
|
|
437
|
+
"status": "pass",
|
|
438
|
+
"fix": null
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"check": "network",
|
|
442
|
+
"status": "fail",
|
|
443
|
+
"fix": "set HTTP_PROXY or check VPN"
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"check": "release_readiness",
|
|
447
|
+
"status": "warn",
|
|
448
|
+
"fix": "record live smoke/E2E evidence before declaring stable"
|
|
449
|
+
}
|
|
450
|
+
]
|
|
451
|
+
},
|
|
452
|
+
"meta": {
|
|
453
|
+
"duration_ms": 0
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
`doctor` 必须包含 `check: "release_readiness"`,并与 `reference` 报告同一个
|
|
459
|
+
发布等级。`stable` 使用 `pass`,有意声明的 `beta` 使用 `warn`,
|
|
460
|
+
`unpublishable` 或自称 `stable` 但证据缺失时使用 `fail`。非 `pass` 状态应给出
|
|
461
|
+
可执行的 `fix`。
|
|
462
|
+
|
|
463
|
+
### changelog
|
|
464
|
+
|
|
465
|
+
报告**版本之间发生了什么变化**,让自更新后的 Agent 能补齐认知,而不是继续用旧套路。这是 `reference`(描述当前能力)在时间维度上的补充。
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
tool changelog # 全部版本变更
|
|
469
|
+
tool changelog --since 1.0.3 # 只返回比 1.0.3 新的版本
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
```json
|
|
473
|
+
{
|
|
474
|
+
"ok": true,
|
|
475
|
+
"schema_version": "1.0",
|
|
476
|
+
"data": {
|
|
477
|
+
"current_version": "1.1.0",
|
|
478
|
+
"since": "1.0.3",
|
|
479
|
+
"entries": [
|
|
480
|
+
{
|
|
481
|
+
"version": "1.1.0",
|
|
482
|
+
"date": "2026-06-07",
|
|
483
|
+
"changes": {
|
|
484
|
+
"added": [
|
|
485
|
+
"..."
|
|
486
|
+
],
|
|
487
|
+
"changed": [
|
|
488
|
+
"..."
|
|
489
|
+
],
|
|
490
|
+
"fixed": []
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
},
|
|
495
|
+
"meta": {
|
|
496
|
+
"duration_ms": 0
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
约定:
|
|
502
|
+
|
|
503
|
+
- **单一真相源**:`changelog` 输出从 `CHANGELOG.md` 派生(构建时按 `## [version]` 段落嵌入二进制),不另维护数据。与 release-notes 同源。
|
|
504
|
+
- `--since <version>` 只返回严格高于该版本的条目,供「上次见过 X 版本」的 Agent 拉增量。
|
|
505
|
+
- 变更分类沿用 Keep a Changelog:`added` / `changed` / `fixed` / `deprecated` / `removed` / `security`。
|
|
506
|
+
- 自更新成功后,工具应在结果中提示 Agent 运行 `changelog --since <旧版本>`(见 §14)。
|
|
507
|
+
|
|
508
|
+
## 12. 命令设计约定
|
|
509
|
+
|
|
510
|
+
1. 用最短命令完成明确任务,减少组合复杂度。
|
|
511
|
+
2. 查询命令默认支持 `--fields` 与 `--compact` 降 token。
|
|
512
|
+
3. 写命令必须支持 `--dry-run` 与 `--confirm`。
|
|
513
|
+
4. 命名采用 `<noun> <verb>` 或 `<verb> <noun>` 风格,并在全局统一。
|
|
514
|
+
5. 不要求 Agent 解析帮助文本;`--help` 给人看,机器能力通过 `reference` 暴露。
|
|
515
|
+
6. 所有时间用 ISO 8601 UTC;所有 ID 用字符串。
|
|
516
|
+
7. 命令失败时优先返回结构化错误,不输出半截成功 payload。
|
|
517
|
+
8. 命令参数应避免二义性;布尔值用 flag,枚举值用有限选项。
|
|
518
|
+
|
|
519
|
+
## 13. 功能契约覆盖率与发布门禁
|
|
520
|
+
|
|
521
|
+
功能契约覆盖率(Functional Contract Coverage,FCC)是发布阻断标准:Agent
|
|
522
|
+
能依赖的每一个公开行为,都必须有自动化命令级测试覆盖。代码行覆盖率或分支覆盖率是有用的工程指标,但它是辅助指标,不能替代缺失的功能契约测试。
|
|
523
|
+
|
|
524
|
+
公开功能契约包括以下位置声明的任何行为:
|
|
525
|
+
|
|
526
|
+
- `README.md` / `README_zh.md`、`SKILL.md` 或 Skill reference 页面;
|
|
527
|
+
- `tool reference`、`--help`、`context`、`doctor`、`changelog`、`update` 输出;
|
|
528
|
+
- JSON envelope 字段、命令输出 schema、全局参数、错误码、exit code、retryable、stdout/stderr 边界;
|
|
529
|
+
- 已记录的配置/环境变量、凭证处理、写操作安全、自更新校验、Skill 同步和 `_untrusted` 安全承诺。
|
|
530
|
+
|
|
531
|
+
每个公开命令或契约至少覆盖:
|
|
532
|
+
|
|
533
|
+
- 成功路径;
|
|
534
|
+
- 缺失/非法参数;
|
|
535
|
+
- 配置缺失、认证缺失或权限失败(适用时);
|
|
536
|
+
- 上游 API 失败、网络失败、限流或超时(适用时);
|
|
537
|
+
- JSON envelope 形状、输出 schema、exit code、stdout/stderr 边界;
|
|
538
|
+
- 非交互行为:不提示、不阻塞,写命令使用 `--dry-run` -> `--confirm <token>`;
|
|
539
|
+
- 每个影响可观察行为的 bug fix 都必须带回归测试。
|
|
540
|
+
|
|
541
|
+
`FCC = 100%` 的含义:
|
|
542
|
+
|
|
543
|
+
- 公开契约中列出的每个命令、参数、输出、错误行为,都映射到至少一个自动化测试,或明确标注为不适用;
|
|
544
|
+
- 命令级测试验证 CLI 边界,而不仅仅测试内部 helper;
|
|
545
|
+
- 生成代码、版本常量、构建元数据或不可达平台保护分支可以从数字覆盖率中排除,但如果它们是文档化公开行为,就不能从 FCC 中排除;
|
|
546
|
+
- 存在已知 FCC 缺口时,不得打发布 tag。
|
|
547
|
+
- `fcc_status: "verified"` 必须有机器背书——枚举守卫测试:从 `reference` 实时输出枚举全部叶子命令,逐一断言存在命令级测试调用。状态诚实声明为 `missing` 时守卫跳过;不补测试就把声明翻成 `verified` 会让守卫立刻失败(模板自带该守卫)。
|
|
548
|
+
|
|
549
|
+
CI 应在每个 PR 上运行单测和命令级测试。数字覆盖率门槛可以按仓库逐步抬升,但发布标准是绝对的:公开功能契约必须被覆盖。
|
|
550
|
+
|
|
551
|
+
### 发布就绪等级
|
|
552
|
+
|
|
553
|
+
发布就绪比「测试通过」更严格:
|
|
554
|
+
|
|
555
|
+
- **Stable**:FCC 达到 100%;mock upstream / contract tests 覆盖成功、
|
|
556
|
+
参数校验、配置/认证/权限失败、上游/网络/限流/超时失败、空结果、分页、输出
|
|
557
|
+
schema、exit code、stdout/stderr 边界;并且该 release candidate 至少有一次
|
|
558
|
+
真实环境 smoke/E2E 记录。
|
|
559
|
+
- **Beta**:FCC 达到 100%,mock upstream / contract tests 具备同等行为宽度,
|
|
560
|
+
但缺少真实环境 smoke/E2E 记录,或项目明确声明暂不具备真实 E2E 条件。
|
|
561
|
+
- **Unpublishable**:任何公开命令、参数、输出或错误行为缺少命令级测试,或
|
|
562
|
+
mock upstream 测试只覆盖 happy path。
|
|
563
|
+
|
|
564
|
+
`reference.release_readiness` 和 `doctor.checks[]` 是这道门禁的机器可读出口。
|
|
565
|
+
仓库可以选择不发布 `beta` 产物,但没有上述真实证据时不得自称 `stable`。
|
|
566
|
+
|
|
567
|
+
## 14. 版本与兼容策略
|
|
568
|
+
|
|
569
|
+
- `schema_version` 表示输出 schema 版本,不等同于工具版本。
|
|
570
|
+
- 破坏性 schema 变更升级主版本,例如 `1.x` -> `2.0`。
|
|
571
|
+
- 非破坏性新增字段可以保持主版本不变。
|
|
572
|
+
- 废弃字段应先保留兼容期,并在 reference 中标记 deprecated。
|
|
573
|
+
- 兼容别名可以存在,但不应作为新文档推荐用法。
|
|
574
|
+
- Agent 应优先依据 `reference`,而不是 `--help` 或 README。
|
|
575
|
+
|
|
576
|
+
### 版本协商(工具版本 ↔ Skill 期望)
|
|
577
|
+
|
|
578
|
+
Skill 是写它那天的能力快照,二进制版本一漂就可能错位:按 v1.1 写的 Skill 碰上 v1.0 二进制,会静默调用不存在的命令。
|
|
579
|
+
|
|
580
|
+
- 工具必须能报告自身版本:`tool --version` 与 `context.data.version`。
|
|
581
|
+
- Skill 在 frontmatter 声明最低兼容版本(见 SKILL-SPEC `requires.min_version`)。
|
|
582
|
+
- `doctor` 应有一项检查「当前版本是否满足声明的最低版本」,不满足时给出 `fix`(升级命令),状态 `fail`。
|
|
583
|
+
|
|
584
|
+
### 自更新与 Skill 同步闭环
|
|
585
|
+
|
|
586
|
+
带 `self-update` 的工具,更新成功后**必须打通两条更新链**:
|
|
587
|
+
|
|
588
|
+
1. 二进制或包本身是最新;
|
|
589
|
+
2. 内置的每个 Agent Skill 目录也是最新,最终状态等同于运行 `npx skills add <repo> -y -g`。
|
|
590
|
+
|
|
591
|
+
用户首次安装 Skill 仍使用 `npx skills add ...`;CLI 二进制不能暴露单独的 `install-skill` 命令。但在 update 生命周期中,工具需要负责完整更新:要么同步 `skills/` 下的每个 Skill 目录(运行 `npx skills add <repo> -y -g` 正是这样;只同步 `skills/<tool>/` 会让工具的其他 Skill(SKILL-SPEC §7)停在旧版本),要么在结果中显式返回 `skill_sync_status` 与 `skill_sync_command`,让 Agent 在使用新能力前完成同步。
|
|
592
|
+
|
|
593
|
+
单命令 update 契约(无叶子子命令、无 confirm token):
|
|
594
|
+
|
|
595
|
+
- 裸 `update` 一次调用完成整个更新:解析最新(或 `--target-version`)release、验证完整性、替换二进制/包、再同步各 Skill 目录。自更新是单目标、非破坏、自验证的操作,因此**豁免 §7 的 `--dry-run → --confirm <token>` 写门禁**——安全保证来自下面的进程内签名验证,而不是 Agent 对预览的审阅。`update` 没有叶子子命令。
|
|
596
|
+
- **按安装方式分派——驱动包管理器执行,而不是只打印命令。**"替换二进制/包"意味着这一次调用要对*每种*安装方式都抵达升级后的终态,而不仅是独立二进制:
|
|
597
|
+
- **独立二进制**(文件归工具自己所有):下载 → 进程内 Sigstore 验签 → checksum → 原地原子替换;`signature_status: "verified"`。
|
|
598
|
+
- **包管理器管理的安装**(npm / Go / Homebrew——文件归包管理器所有):工具**不得**原地修改被管理的文件(会让包管理器的元数据失配),也**不得**仅把命令返回给用户去跑。它要**驱动**包管理器——替用户执行安装命令(如 `npm install -g <pkg>@<version>`),再同步 Skill,抵达同样的终态、`status: "updated"`。这条路径的完整性归包管理器自己(registry 完整性/provenance),所以 `signature_status` 为 `not_checked`;新版本在下次调用时生效。安装方式检测必须稳健(别把独立二进制误判为被管理的);包管理器调用失败时返回错误、`binary_replaced: false` 并附上可手动执行的确切命令。
|
|
599
|
+
- `update --check` 是**可选只读**探针:返回当前/目标版本、安装方式、是否有更新、Skill 同步是否可用、checksum/签名材料是否可用。它不改任何东西。
|
|
600
|
+
- `update --dry-run` 是同样变更(二进制/包更新、Skill 同步、验证计划)的**可选只读**预览。它**不签发 token**,且绝不是 `update` 的前置必经步骤。
|
|
601
|
+
- `update` 幂等:已是最新(或所请求)版本时返回 `ok` + no-op 结果,Agent 可放心反复调用。这个 no-op 判断**必须**先于任何包管理器命令执行;已经是当前版本时不得重跑 `npm`、`go`、`pip`、`brew` 或同类管理器。
|
|
602
|
+
- 成功与 no-op 的 `update` 结果描述的是命令执行后的**终态**,不是安装前的比较。成功安装后,`data` 携带 `previous_version`、`current_version`、`target_version`、`update_available`、`signature_verified`、`signature_status`、`skill_sync_status`,以及足够审计的校验元数据。`current_version` **必须等于** `target_version`,`update_available` **必须为** `false`。no-op 结果中 `current_version` 与 `target_version` 也相等,`update_available` 为 `false`。
|
|
603
|
+
- 如果二进制/包更新成功但 Skill 同步失败,返回部分成功(`ok: false`、`binary_replaced: true`),并给出 `target_version`、`update_available: false` 与 `skill_sync_command`;Agent 在 Skill 同步完成前不得使用新文档能力。
|
|
604
|
+
|
|
605
|
+
版本通知契约:
|
|
606
|
+
|
|
607
|
+
- `update --check` 主动检查最新 release,并刷新本地更新通知缓存。
|
|
608
|
+
- `doctor` 可以用较短超时主动检查;网络失败本身不得导致 `doctor` 失败。
|
|
609
|
+
- `context` 和 `--help` 只读取本地缓存,不得访问远程 registry 或 GitHub。
|
|
610
|
+
- 缓存里的通知 MAY 同时挂到**每条命令的 `meta.notices`**,**只从本地缓存读**
|
|
611
|
+
(不联网;代价仅一次本地文件读取)。业务命令只是把缓存通知透出来——绝不主动检查 / 回家。
|
|
612
|
+
缓存无内容可报时省略 `meta.notices`。
|
|
613
|
+
- 成功安装、二进制/包已提交后的部分成功,或目标/最新版本上的幂等 no-op 之后,工具**必须**清除或压制该已安装目标对应的缓存 `update_available` 通知,避免后续命令继续挂出 `meta.notices`。`update` 命令自己的响应也不得携带“刚安装的目标仍可更新”的陈旧通知。
|
|
614
|
+
- 有可用更新时,通知含 `type: "update_available"`、`severity`、当前/最新版本、安装方式、
|
|
615
|
+
`recommended_command`、已知 release URL、检查时间和机器可读的下一步。它出现在主动检查类命令的
|
|
616
|
+
`data`(`context` / `doctor` / `update --check`),并以只读方式从缓存出现在任意命令的
|
|
617
|
+
`meta.notices`。文本/help 输出可追加一句简短提示。
|
|
618
|
+
- **severity 分级**——在检查时由内嵌 CHANGELOG 在「本机版本 → 最新」之间的增量算出,
|
|
619
|
+
并写入缓存,使缓存的 `meta.notices` 自带正确级别:
|
|
620
|
+
- `info`(默认):例行 patch/minor,增量里无 security 条目。
|
|
621
|
+
- `warning`:自本机版本以来的 changelog 增量含 `security` 条目,**或**最新跨了
|
|
622
|
+
**major** 版本(首位 semver 增大)——即很可能涉及安全或破坏性变更。
|
|
623
|
+
|
|
624
|
+
release 校验基线:
|
|
625
|
+
|
|
626
|
+
- **强制签名验证,无跳过路径**:二进制自更新路径必须在进程内验证 `checksums.txt` 的 Sigstore 签名,再用它校验归档 SHA256。签名 bundle 缺失、签名验不过、checksum 不符,一律**失败关闭**,不存在"验不了就放行"的降级。整条链对外返回 `E_INTEGRITY`(exit 1,非重试)——伪造或损坏的发布不该被当成可重试的瞬时错误。
|
|
627
|
+
- **验证器内置、不依赖用户环境**:验证在工具二进制内完成(Go 用 `sigstore-go`,Python 冻结二进制内用 `sigstore`),**不外挂 cosign**,不依赖机器上预装任何东西。TUF 信任根从库内嵌的 `root.json` 引导(不是 TOFU 现拉现信)。
|
|
628
|
+
- **新版 bundle 格式**:签名侧用 `cosign sign-blob --new-bundle-format` 产出 Sigstore protobuf bundle(`checksums.txt.sigstore.json`),与进程内验证器对齐;旧版 cosign bundle 格式不被接受。
|
|
629
|
+
- **身份绑定**:验证端把证书 SAN 绑定到本仓库的 tagged release workflow(`…/release.yml@refs/tags/v*`,锚定 `^…$`)并校验 GitHub OIDC issuer。已知目标 tag 时可绑精确身份,强于正则。
|
|
630
|
+
- **跨语言统一**:Go 二进制与 Python 冻结二进制走同一套自更新契约——都是"下载归档 → 进程内验签 → checksum → 替换二进制",包管理器不参与完整性。
|
|
631
|
+
- update 结果携带 `signature_status`(成功即 `verified`;异常一律走 error envelope 中止)与 `signature_verified`(仅当进程内 Sigstore 验证真实执行且成功时为 true)。不能把 checksum 校验伪装成签名校验。
|
|
632
|
+
|
|
633
|
+
- `update` 成功后,结果 `data` 中返回 `previous_version` 与 `current_version`。
|
|
634
|
+
- 同时在结果中提示:`run "changelog --since <previous_version>" to see what changed`。
|
|
635
|
+
- Agent 约定:自更新后、继续干活前,先读 `changelog --since <旧版本>`(见 SKILL-SPEC 配方)。
|
|
636
|
+
|
|
637
|
+
失败与中断契约:
|
|
638
|
+
|
|
639
|
+
单命令 `update` 按阶段执行——发现 → 下载 → 验签 → 验 checksum → 替换二进制 → 同步 Skill——只有一个原子提交点。让每条失败 message 都诚实的不变式:
|
|
640
|
+
|
|
641
|
+
- 替换二进制**之前**的一切只动临时目录;这里任何失败或中断都让已装二进制纹丝不动、完全可用(`current_version` 不变、`binary_replaced: false`)。
|
|
642
|
+
- 替换本身是原子的。Windows 上正在运行的可执行文件可被 rename 但不可覆写,因此把验好的 `<bin>.new` 落位的方式是:把在用的二进制 rename 到一旁 `<bin>.old`,再把 `<bin>.new` rename 到真实路径——**新二进制当场就位、`binary_replaced: true`,下次调用即运行新版(无需重启)**。被挤掉的 `<bin>.old` 在旧进程仍运行时删不掉,故留在原地、由下次 `update` 清理(并从头重验任何 staged 产物,遗留绝不被信任)。非 Windows 上则把验好的二进制同盘 rename 落位。替换中途崩溃只会留下旧的或新的二进制,绝无半截。
|
|
643
|
+
- Skill 同步在替换**之后**执行,且可独立重放。
|
|
644
|
+
|
|
645
|
+
因此工具永远能确定、也**必须始终报告**它失败后的状态。每个 update 失败信封在 `error.details`(部分成功时在 `data`)携带:`stage`(`discover|download|verify_signature|verify_checksum|replace|skill_sync`)、`current_version`、`binary_replaced`、`skill_sync_status`。
|
|
646
|
+
|
|
647
|
+
按 **Agent 的下一步动作**分类失败,而不是按原始成因:
|
|
648
|
+
|
|
649
|
+
| 阶段 | 失败 | code / exit | retryable | 失败后状态 | message 必须说 |
|
|
650
|
+
|------|------|-------------|-----------|------------|----------------|
|
|
651
|
+
| discover / download | 网络 / 超时 / 限流 | `E_NETWORK` / `E_TIMEOUT` / `E_RATE_LIMITED` → 7,8 | true | 旧版本,无改动 | "瞬时——重跑 `update`,它幂等" |
|
|
652
|
+
| verify_signature / verify_checksum | 签名缺失/无效、身份不符、checksum 不符 | `E_INTEGRITY` → 1 | **false** | 旧版本,已拒装 | "完整性失败——不要重试,停下报人" |
|
|
653
|
+
| replace | 权限 / 磁盘满 / 文件被占 | `E_FORBIDDEN` / `E_CONFIG` / `E_IO` → 4,1 | false(需修) | 旧版本(原子未提交) | 给出具体修法,再重跑 |
|
|
654
|
+
| skill_sync(替换后) | npx 缺失 / 网络 | 部分成功(`ok:false`、`binary_replaced:true`) | true | 二进制新、Skill 旧 | "二进制已到 vX;运行 `<skill_sync_command>`,再 `changelog --since <prev>`" |
|
|
655
|
+
| 任意 | 用户/信号中断(SIGINT) | `E_INTERRUPTED` → 130 | true | 见上述阶段不变式 | 实际发生了什么 + 安全的下一步 |
|
|
656
|
+
|
|
657
|
+
中断(Ctrl-C / SIGTERM):
|
|
658
|
+
|
|
659
|
+
- 捕获信号,把当前阶段回退到干净状态,并在退出前**仍向 stdout 吐出终态 JSON 信封**——被中断的 Agent 必须拿到可解析的终态,而不是一个被杀的裸进程。
|
|
660
|
+
- 中断时一律清理临时目录;半截下载绝不可被后续运行信任(永远重新下载 + 重新验签)。
|
|
661
|
+
- message 取决于被中断的阶段:替换前 → "已取消,无改动,仍在 `<current>`";原子替换中 → 据实报旧或新;替换后 Skill 同步中 → 部分成功 + `skill_sync_command`。
|
|
662
|
+
|
|
663
|
+
message 永不可破的三条铁律:
|
|
664
|
+
|
|
665
|
+
1. 绝不谎报版本:每个终态信封都说明工具现在实际运行的版本。
|
|
666
|
+
2. 绝不让 Agent 重试完整性失败:`E_INTEGRITY` 是 `retryable: false`,且措辞上与任何网络失败明确区分——伪造的 release 不是可循环重试的瞬时抖动。
|
|
667
|
+
3. 绝不把部分当成功:二进制已换但 Skill 未同步是部分成功 + `skill_sync_command`,不是 `ok: true`。
|
|
668
|
+
|
|
669
|
+
## 15. 批量操作(Batch operations)
|
|
670
|
+
|
|
671
|
+
很多写操作的现实工作流需要一次对**一批**对象执行(关一批 issue、群发给一批 openid、对一批实例跑同一条 SQL)。批量命令对 Agent 仍是**一条**命令、**一个** envelope、**一个** confirm token、**一份**聚合结果——绝不是要 Agent 自己驱动的循环。下面的契约无论该批量由上游原生 bulk 端点服务(A 类)还是客户端循环实现(B 类)都完全一致;Agent 不应、也不需要分辨是哪一类。
|
|
672
|
+
|
|
673
|
+
### 15.1 复数入参
|
|
674
|
+
|
|
675
|
+
- 批量目标用复数参数:`--ids`、`--symbols`、`--instances`、`--openids` 等。
|
|
676
|
+
- 每个复数参数同时接受 **comma-separated**(`--ids 1,2,3`)与 **repeatable**(`--ids 1 --ids 2 --ids 3`)两种写法;二者等价,可混用。
|
|
677
|
+
- 单值优雅退化:`--ids 1` 是合法的「批量一个」,与批量多个同形 envelope。已存在单数参数(`--symbol`)的,保留为复数的兼容别名并在 reference 标 deprecated,不维护两条分叉代码路径。
|
|
678
|
+
- 执行前对目标去重,并在结果 `items[]` 中保持输入顺序,使 Agent 能确定性地把结果对回输入。
|
|
679
|
+
- 空目标列表是用法错误(`E_VALIDATION`,exit 2),不是静默 no-op。
|
|
680
|
+
|
|
681
|
+
### 15.2 批量的 dry-run 汇总
|
|
682
|
+
|
|
683
|
+
批量的 `--dry-run` 在任何写之前返回**将对 N 个对象做什么**,并给出一个覆盖整批的 `confirm_token`:
|
|
684
|
+
|
|
685
|
+
```json
|
|
686
|
+
{
|
|
687
|
+
"ok": true,
|
|
688
|
+
"schema_version": "1.0",
|
|
689
|
+
"data": {
|
|
690
|
+
"preview": {
|
|
691
|
+
"action": "close",
|
|
692
|
+
"total": 3,
|
|
693
|
+
"targets": ["1042", "1043", "1044"],
|
|
694
|
+
"changes": [
|
|
695
|
+
{ "action": "close", "resource": "issue", "id": "1042" },
|
|
696
|
+
{ "action": "close", "resource": "issue", "id": "1043" },
|
|
697
|
+
{ "action": "close", "resource": "issue", "id": "1044" }
|
|
698
|
+
]
|
|
699
|
+
},
|
|
700
|
+
"confirm_token": "ct_9f2a...",
|
|
701
|
+
"expires_at": "2026-06-15T12:00:00Z"
|
|
702
|
+
},
|
|
703
|
+
"meta": { "duration_ms": 0 }
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
- preview 必须说明操作与完整目标集合,让人或 Agent 在确认前审计影响范围(blast radius)。
|
|
708
|
+
- token 绑定**整个已解析的目标集合**(外加命令路径、参数、账号、权限上下文,见 §7),增减任一目标即令其失效并返回 `E_CONFLICT`。
|
|
709
|
+
|
|
710
|
+
### 15.3 单个 confirm token 覆盖整批,且单次消费
|
|
711
|
+
|
|
712
|
+
- 批量 dry-run 给出的单个 `--confirm <token>` 授权整批;Agent 不逐项确认。
|
|
713
|
+
- token **单次消费**,与 §9 完全一致:写执行前记录指纹并标记已消费,任何重放以 `E_CONFLICT` 拒绝(「token 已用过,请重新 `--dry-run`」)。这沿用各仓现有的 confirm 单次消费基础设施——批量不新增任何 token 机制。
|
|
714
|
+
- 批量部分失败后,token 仍保持已消费;Agent 重新 `--dry-run`(此时只解析到仍待处理的目标),而不是重放旧 token。
|
|
715
|
+
|
|
716
|
+
### 15.4 危险批量:`--dangerous` 两步闸门
|
|
717
|
+
|
|
718
|
+
不可逆或高影响范围的批量——批量 `delete`、MR `merge`、群发 / 广播——在 dry-run → confirm **之上**再加一道闸门:
|
|
719
|
+
|
|
720
|
+
- 命令必须带 `--dangerous` 调用;不带时即使持有有效 confirm token,命令也以 `E_CONFIRMATION_REQUIRED`(exit 5)失败。
|
|
721
|
+
- 这是两步人意闸门:`--dangerous` 声明意图,confirm token 授权具体已解析的那一批。两者缺一不可,任何一个单独都不执行。
|
|
722
|
+
- reference 把这些命令标 `dangerous: true`,其 `examples[]` 展示 `--dangerous` 形式。
|
|
723
|
+
- 工具可在危险批量上叠加更严格的本地策略(如逐项 confirm、默认 `--continue-on-error false`、夜间仅 dry-run);此类覆盖必须在 reference 声明,不得暗藏。
|
|
724
|
+
|
|
725
|
+
### 15.5 逐项聚合结果,不整体回滚
|
|
726
|
+
|
|
727
|
+
批量结果按项聚合。部分失败**不**回滚已成功的项:
|
|
728
|
+
|
|
729
|
+
```json
|
|
730
|
+
{
|
|
731
|
+
"ok": true,
|
|
732
|
+
"schema_version": "1.0",
|
|
733
|
+
"data": {
|
|
734
|
+
"items": [
|
|
735
|
+
{ "target": "1042", "ok": true },
|
|
736
|
+
{ "target": "1043", "ok": true },
|
|
737
|
+
{
|
|
738
|
+
"target": "1044",
|
|
739
|
+
"ok": false,
|
|
740
|
+
"error": { "code": "E_NOT_FOUND", "retryable": false }
|
|
741
|
+
}
|
|
742
|
+
],
|
|
743
|
+
"summary": { "total": 3, "succeeded": 2, "failed": 1 }
|
|
744
|
+
},
|
|
745
|
+
"meta": { "duration_ms": 0 }
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
- 每个 `items[]` 项带 `target`(输入标识——`id`、`symbol`、`instance`……;用自然键,不用数组下标)、`ok`,失败时带 `error`,其 `{ code, retryable }` 分类与顶层 envelope(§6)一致。
|
|
750
|
+
- `summary` 始终报告 `{ total, succeeded, failed }`,计数必须与 item 实际数量相等。
|
|
751
|
+
- 只要批量执行并产出结果,顶层 `ok` 即为 `true`,即便存在逐项失败;逐项状态在 `items[]` 里。顶层 `ok: false` 仅保留给整批根本无法执行的情况(参数错、鉴权、无目标)。不要因为单项失败而隐藏其他项状态(与 §9 一致)。
|
|
752
|
+
- `--continue-on-error` 控制首个失败后是否继续;**默认 `true`**(尽力而为,跑完整批)。设 `--continue-on-error false` 在首个失败处停止——已应用的项保持已应用(不回滚),`summary` 只反映已尝试的项,未尝试的剩余项要报告出来(如 `skipped`),让 Agent 可续跑。危险批量可把默认翻成 `false`,需在 reference 声明。
|
|
753
|
+
|
|
754
|
+
### 15.6 上游有上限:客户端自动分批
|
|
755
|
+
|
|
756
|
+
当原生 bulk 端点有单次调用上限时,命令把批量切成多块顺序提交,对 Agent 仍呈现为一条命令:
|
|
757
|
+
|
|
758
|
+
- 已知上限:Jira `/issue/bulk`、agile `backlog/issue` 与 `sprint/{id}/issue` ≤ 50;微信 openid 批量 ≤ 100,群发 openid 列表按上游上限。命令必须按上限分批,不得把超限批量直接透传让上游 400。
|
|
759
|
+
- 分批在契约里不可见:跨所有块仍是一个 envelope、一个 `confirm_token`、一份聚合 `items[]`/`summary`。
|
|
760
|
+
- 块级失败映射回受影响的 `items[]`;单块失败不令整条命令失败(受 `--continue-on-error` 约束)。
|
|
761
|
+
- 把分批大小收敛到每个工具的一个共享 helper 里,避免共用同一端点的命令间上限漂移。
|
|
762
|
+
|
|
763
|
+
### 15.7 A/B 两类,对外契约一致
|
|
764
|
+
|
|
765
|
+
- **A 类**用上游原生 bulk 端点(真服务端批量,上游可能原子)。
|
|
766
|
+
- **B 类**因无原生 bulk,客户端对单目标调用做循环。
|
|
767
|
+
- 对外契约对两类完全一致:复数入参、dry-run 汇总、单 confirm token、危险闸门、聚合 `items[]`/`summary`、`--continue-on-error`。Agent 无法也无需分辨 A 与 B。
|
|
768
|
+
- 原子性**不**是对外契约的一部分。B 类(或被分批的、有上限的 A 类)批量不得宣称上游原子;上游确实非原子或顺序不稳定时,在 output schema / reference 里如实说明,而不是暗示事务语义。
|
|
769
|
+
|
|
770
|
+
### 15.8 批量命令的自描述
|
|
771
|
+
|
|
772
|
+
每个批量命令带真实的 `output_schema` 与可运行 `examples[]`(见 §11):
|
|
773
|
+
|
|
774
|
+
- schema 声明 `items[]` 形状(`target`、`ok`、`error{code,retryable}`)与 `summary{total,succeeded,failed}` 形状,攻击者可控的键列入 `untrusted_fields`(`_untrusted`)。
|
|
775
|
+
- `examples[]` 展示复数入参的 dry-run 再 confirm 这一对;危险批量带 `--dangerous`。
|
|
776
|
+
- 这些必须通过 reference 守卫(每个叶子命令非空 schema + 至少一条 example),并像其他公开行为一样计入 FCC(§13)。
|
|
777
|
+
|
|
778
|
+
## 16. 可选模式(按需启用)
|
|
779
|
+
|
|
780
|
+
以下三种模式**不是人人必做**:工具用得上就照这里实现,用不上就忽略,零负担。它们让规范随工具复杂度伸缩——简单工具保持轻,复杂工具不必重新发明轮子。每条都标了「何时适用」。
|
|
781
|
+
|
|
782
|
+
### 16.1 凭证生命周期(令牌会过期时)
|
|
783
|
+
|
|
784
|
+
**何时适用**:凭证不是静态的,而是会过期 / 需刷新的——OAuth access_token(微信公众号约 2h)、cookie / session(小红书)、临时 STS 凭证等。静态用户名密码的工具跳过本节。
|
|
785
|
+
|
|
786
|
+
- `context.data.credentials` 除「配没配」外,应报告**有效性与过期信息**(脱敏):
|
|
787
|
+
|
|
788
|
+
```json
|
|
789
|
+
{
|
|
790
|
+
"credentials": {
|
|
791
|
+
"configured": true,
|
|
792
|
+
"valid": true,
|
|
793
|
+
"expires_at": "2026-06-07T12:00:00Z",
|
|
794
|
+
"refreshable": true
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
- 令牌过期且不可自动刷新时,操作返回 `E_AUTH`(exit 4),`details` 指明需重新认证。
|
|
800
|
+
- 可自动刷新的工具应**透明刷新**,不让 Agent 操心;刷新失败再降级为 `E_AUTH`。
|
|
801
|
+
- `doctor` 增一项 `check: "credentials"`,对临近过期给 `warn` + 续期 `fix`。
|
|
802
|
+
- 刷新令牌、secret 一律脱敏,绝不出现在 stdout / stderr / details。
|
|
803
|
+
|
|
804
|
+
### 16.2 异步任务生命周期(长任务:提交 -> 轮询 -> 取结果)
|
|
805
|
+
|
|
806
|
+
**何时适用**:操作不能同步返回结果——SQL 异步执行 / 审批(Archery)、批量群发、采集 / 爬取任务、大导出。同步即得结果的命令跳过本节。
|
|
807
|
+
|
|
808
|
+
- 提交命令立即返回 `job_id` 与状态,不阻塞:
|
|
809
|
+
|
|
810
|
+
```json
|
|
811
|
+
{
|
|
812
|
+
"ok": true,
|
|
813
|
+
"schema_version": "1.0",
|
|
814
|
+
"data": {
|
|
815
|
+
"job_id": "job_abc123",
|
|
816
|
+
"status": "pending",
|
|
817
|
+
"poll": "tool job status --id job_abc123",
|
|
818
|
+
"result": "tool job result --id job_abc123"
|
|
819
|
+
},
|
|
820
|
+
"meta": { "duration_ms": 12 }
|
|
821
|
+
}
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
- 状态查询返回稳定枚举:`pending` / `running` / `succeeded` / `failed` / `cancelled`,并带进度(如 `progress`、`eta_seconds`)。
|
|
825
|
+
- 结果与状态分开取:`succeeded` 后用 `result` 命令拉数据(大结果走 NDJSON / `--format raw`)。
|
|
826
|
+
- `failed` 的结果用标准 error envelope,`retryable` 指明能否重试整个任务。
|
|
827
|
+
- 写类长任务的提交仍走 `dry-run → confirm`;confirm 后才落 `job_id`。
|
|
828
|
+
|
|
829
|
+
### 16.3 人工介入检查点(需要人来扫码 / 验证码 / 审批时)
|
|
830
|
+
|
|
831
|
+
**何时适用**:流程中途必须由人完成某步——扫码登录 / 验证码(小红书)、审批人放行(Archery)、二次确认等。全自动工具跳过本节。
|
|
832
|
+
|
|
833
|
+
- 卡在人工步骤时,**不阻塞、不瞎试**,返回专用信号让 Agent 把球踢给用户:
|
|
834
|
+
|
|
835
|
+
```json
|
|
836
|
+
{
|
|
837
|
+
"ok": false,
|
|
838
|
+
"schema_version": "1.0",
|
|
839
|
+
"error": {
|
|
840
|
+
"code": "E_HUMAN_REQUIRED",
|
|
841
|
+
"message": "Scan the QR code to continue",
|
|
842
|
+
"details": { "action": "scan_qr", "resume": "tool login resume --id sess_1", "qr_path": "/tmp/qr.png" },
|
|
843
|
+
"retryable": false
|
|
844
|
+
},
|
|
845
|
+
"meta": { "duration_ms": 30 }
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
- `E_HUMAN_REQUIRED` 用 exit code `9`(在既有 0–8 之外新增;不复用 `4`,以区分「凭证错」与「等人动作」)。
|
|
850
|
+
- `details.action` 用稳定枚举说明需要人做什么,`details.resume` 给出人工完成后的续跑命令。
|
|
851
|
+
- Agent 约定:收到 `E_HUMAN_REQUIRED` → 向用户转述 `message` 与所需动作 → 等用户完成 → 跑 `resume`,不自动重试。
|
|
852
|
+
|
|
853
|
+
## 17. 设计检查清单
|
|
854
|
+
|
|
855
|
+
> 标 `(可选)` 的仅当对应可选模式启用时才需勾。
|
|
856
|
+
|
|
857
|
+
- [ ] 默认 `--format json`
|
|
858
|
+
- [ ] stdout 仅含合法 JSON / NDJSON,无污染
|
|
859
|
+
- [ ] 日志与进度全部走 stderr
|
|
860
|
+
- [ ] 成功/失败同形 envelope,含 `ok` 与 `schema_version`
|
|
861
|
+
- [ ] `error` 含语义化 `code`、`details`、`retryable`
|
|
862
|
+
- [ ] exit code 分层且与 `retryable` 一致
|
|
863
|
+
- [ ] 写命令具备 dry-run / confirm-token 闭环
|
|
864
|
+
- [ ] confirm token 绑定操作参数、账号、权限上下文和资源版本
|
|
865
|
+
- [ ] 提供 `reference` / `context` / `doctor`
|
|
866
|
+
- [ ] 提供 `changelog [--since]`,与 CHANGELOG/release-notes 同源
|
|
867
|
+
- [ ] 工具可报告自身版本(`--version` 与 `context.version`)
|
|
868
|
+
- [ ] `reference` 报告 `release_readiness`,`doctor` 检查它
|
|
869
|
+
- [ ] (含 self-update 时)`update` 是单命令(无叶子子命令、无 confirm token);`--check` / `--dry-run` 为可选只读
|
|
870
|
+
- [ ] (含 self-update 时)release 完整性被校验,签名状态显式返回
|
|
871
|
+
- [ ] (含 self-update 时)`skills/` 下每个 Skill 目录的同步纳入 update 结果
|
|
872
|
+
- [ ] (含 self-update 时)更新后回传 previous/current 版本并提示读 changelog
|
|
873
|
+
- [ ] (含 self-update 时)每个 update 失败/中断信封携带 `stage` + `current_version` + `binary_replaced` + `skill_sync_status`;`E_INTEGRITY` 非重试;二进制已换但 Skill 未同步是部分成功而非 `ok`
|
|
874
|
+
- [ ] (含 self-update 时)捕获 SIGINT/SIGTERM,不留半截状态,且仍吐出终态 JSON 信封
|
|
875
|
+
- [ ] 查询命令支持 `fields` / `compact`
|
|
876
|
+
- [ ] 列表命令支持分页或明确说明无需分页
|
|
877
|
+
- [ ] 批量命令用复数入参(`--ids`/`--symbols`/…),comma-separated 或 repeatable,单值退化
|
|
878
|
+
- [ ] 批量 dry-run 汇总完整目标集;单个 confirm token 覆盖整批且单次消费
|
|
879
|
+
- [ ] 危险批量(delete / merge / 群发)需 `--dangerous` 两步闸门
|
|
880
|
+
- [ ] 批量结果聚合 `items[].{target,ok,error{code,retryable}}` + `summary{total,succeeded,failed}`,不整体回滚,`--continue-on-error` 默认 true
|
|
881
|
+
- [ ] 有上限的原生 bulk 由客户端自动分批、对外单条命令;A/B 两类对外契约一致;批量命令带真实 `output_schema` + `examples`
|
|
882
|
+
- [ ] README / Skill / reference / help / context / doctor / changelog / update 中声明的公开行为达到 100% 功能契约覆盖率
|
|
883
|
+
- [ ] Stable release 有真实环境 smoke/E2E 记录;否则工具声明为 `beta`
|
|
884
|
+
- [ ] 所有时间为 ISO 8601 UTC
|
|
885
|
+
- [ ] 所有 ID 为字符串
|
|
886
|
+
- [ ] 敏感信息全链路脱敏
|
|
887
|
+
- [ ] schema 变更有版本与兼容策略
|
|
888
|
+
- [ ] stdout/stderr 为 UTF-8 无 BOM
|
|
889
|
+
- [ ] (可选·令牌过期)`context`/`doctor` 报告凭证有效性与过期,刷新失败降级 `E_AUTH`
|
|
890
|
+
- [ ] (可选·长任务)提交返回 `job_id` + 状态枚举,状态/结果分离
|
|
891
|
+
- [ ] (可选·需人工)卡人工步骤返回 `E_HUMAN_REQUIRED`(exit 9)+ `resume`,不自动重试
|