@fieldwangai/agentflow 0.1.134 → 0.1.136

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.
@@ -0,0 +1,558 @@
1
+ # AgentFlow Workflow Report 接入协议
2
+
3
+ ## 目录
4
+
5
+ 1. 接入边界
6
+ 2. 认证、身份与权限
7
+ 3. 数据区域模型
8
+ 4. GET /api/workflows/state
9
+ 5. POST /api/workflows/report
10
+ 6. POST /api/workflow-artifacts/publish
11
+ 7. 字段模型
12
+ 8. 覆盖、合并与删除规则
13
+ 9. 并发、幂等与错误码
14
+ 10. 三个关键接入场景
15
+ 11. prd-flow 参考映射
16
+ 12. 验收清单
17
+
18
+ ## 1. 接入边界
19
+
20
+ 新接入只使用以下三个正式接口:
21
+
22
+ | 方法 | 路径 | 用途 | 是否修改 Workflow |
23
+ | --- | --- | --- | --- |
24
+ | `GET` | `/api/workflows/state` | 读取当前快照和并发 revision | 否 |
25
+ | `POST` | `/api/workflows/report` | 上报全局信息、Action、普通产物、迭代归属和自定义区域 | 是 |
26
+ | `POST` | `/api/workflow-artifacts/publish` | 把本地 Markdown 内容发布成浏览器可访问的预览链接 | 是 |
27
+
28
+ `agentflow-workflow-report` 是接入规格;`workflow-report-client.mjs` 是可复用客户端;`agentflow-cli` 是命令行包装;AgentFlow 服务才负责鉴权、存储、合并和展示。Skill 不参与运行时传输,CLI 也不是数据生产方。
29
+
30
+ 接入方负责采集业务系统事实并解释业务含义。例如 prd-flow 会读取 TAPD、ai-doc、GitLab 和 Jenkins;其他接入方可以读取完全不同的数据源。AgentFlow 不会替接入方修改这些上游系统。
31
+
32
+ 当前服务端仅支持 `tapd` Workflow namespace。稳定身份为 `tapd:<short-id>`,标题、版本名或阶段名都不能作为 Workflow 身份。这是“当前身份适配器的边界”,不是 Workflow Report 数据模型只能描述 TAPD;其他 namespace 要先扩展服务端身份、协作和存储适配器,不能只改请求字符串。
33
+
34
+ ## 2. 认证、身份与权限
35
+
36
+ ### 2.1 认证
37
+
38
+ 使用 Bearer Token:
39
+
40
+ ```http
41
+ Authorization: Bearer <AGENTFLOW_TOKEN>
42
+ Content-Type: application/json
43
+ ```
44
+
45
+ CLI 从 `AGENTFLOW_TOKEN` 或 `AGENTFLOW_SESSION_TOKEN` 读取凭证。不得把 Token 放入请求 JSON、Action、Artifact、日志或代码仓库。
46
+
47
+ ### 2.2 权限
48
+
49
+ | 身份 | 读取 | 上报 / 发布预览 | 管理成员与分享 |
50
+ | --- | --- | --- | --- |
51
+ | Workflow owner | 是 | 是 | 是 |
52
+ | 显式 editor | 是 | 是 | 否 |
53
+ | 显式 viewer | 是 | 否 | 否 |
54
+ | owner 同团队成员 | 是,团队视图自动获得 viewer 权限 | 否 | 否 |
55
+ | 分享链接访问者 | 是 | 否 | 否 |
56
+ | 超级管理员代看 | 是 | 否,只读审阅 | 否 |
57
+
58
+ 首次由已认证用户上报一个尚未登记的 TAPD ID 时,该用户成为这个 Workflow 的 owner。后续写入解析到 owner 的状态空间;没有写权限的调用返回 `403`,不会回退成调用者自己的副本。
59
+
60
+ ## 3. 数据区域模型
61
+
62
+ Workflow 页面由三类数据区域组成:
63
+
64
+ ### 3.1 全局区域
65
+
66
+ 描述“这个需求现在是什么”:标题、状态、负责人、平台、当前分支、研发进度、版本原始事实等。
67
+
68
+ - 完整的生产方观察放在 `observation.state`。
69
+ - 可独立增量更新的生产方事实放在 `globalState`。
70
+ - 个人/团队迭代所需的版本、Sprint、里程碑归属放在 `projections.timeline`。
71
+
72
+ `globalState` 是生产方拥有的事实;`projections` 是可从事实重建的通用索引,不能反过来作为业务真相。
73
+
74
+ ### 3.2 Action 时间轴
75
+
76
+ 描述“关键阶段发生了什么”:方案确认、开始实现、MR 创建、提测、发布完成等。
77
+
78
+ - 阶段本身使用 `action`。
79
+ - MR、构建、测试报告、外部文档链接使用 `artifacts`。
80
+ - `action.key` 是稳定阶段身份;同一个 key 的重复上报更新同一阶段,而不是制造一条新业务阶段。
81
+
82
+ Action 是业务节点,不是运行日志。轮询、刷新、重试等技术动作不应各自创建 Action。
83
+
84
+ ### 3.3 自定义区域
85
+
86
+ 描述只有某个接入实现才理解的结构化面板,例如 prd-flow 的 AI Docs 和 Issues。
87
+
88
+ - 数据放入 `extensions["<producer-namespace>"]`。
89
+ - namespace 必须为小写稳定标识,例如 `prd-flow`。
90
+ - AgentFlow 对未知扩展按不透明 JSON 保存;只有注册了渲染器的 namespace 才会显示成专用面板。
91
+
92
+ AI Docs / Issues 不是通用固定字段。当前唯一注册的 extension renderer 是 `prd-flow`,它识别 AI Docs 链接列表和带父子层级、平台、MR 状态及关联链接的 Issues 树。其他 namespace 会被保存并参与 revision,但不会自动出现页面。
93
+
94
+ 普通的负责人、平台、分支、风险列表和文档链接不需要 extension。优先使用下面的 `globalState.sections` 通用渲染器;只有现有组件无法表达的树形结构、复杂交互或专用业务面板,才定义新的 extension schema 和前端 renderer。
95
+
96
+ ### 3.4 当前可直接使用的通用渲染器
97
+
98
+ | 页面组件 | 上报字段 | 展示样式 | 是否需要前端开发 |
99
+ | --- | --- | --- | --- |
100
+ | 需求概览 | `globalState.title/url/status` | 标题、外链和状态标签 | 否 |
101
+ | 自定义概览分区 | `globalState.sections` | 分区卡片与固定字段样式 | 否 |
102
+ | Action 时间轴 | `action` | 按日期分组的状态点、时间、标题、详情和维度标签 | 否 |
103
+ | Action 产物 | `artifacts[scope=action]` | Action 下的链接按钮 | 否 |
104
+ | 关联产物 | `artifacts[scope=global]` | 侧栏链接列表,展示标题和产物类型 | 否 |
105
+ | 迭代时间线 | `projections.timeline` | 版本/Sprint/里程碑时间线卡片与筛选 | 否 |
106
+ | prd-flow AI Docs / Issues | `extensions["prd-flow"]` | 文档链接列表、层级 Issue 卡片 | 已注册,仅供 prd-flow schema |
107
+ | 其他专用面板 | `extensions["<namespace>"]` | 由接入方设计 | 是,需要注册 schema、空态/错误态、响应式样式和 renderer |
108
+
109
+ `globalState.sections` schema:
110
+
111
+ ```json
112
+ {
113
+ "globalState": {
114
+ "mode": "merge",
115
+ "patch": {
116
+ "title": "Remote Config 拉取频控",
117
+ "url": "https://tapd.example.test/1020124",
118
+ "status": "实现中",
119
+ "sections": {
120
+ "ownership": {
121
+ "title": "归属信息",
122
+ "fields": {
123
+ "owner": {
124
+ "label": "负责人",
125
+ "type": "user",
126
+ "value": { "username": "alice" }
127
+ },
128
+ "platforms": {
129
+ "label": "平台",
130
+ "type": "chips",
131
+ "value": ["Android", "iOS"]
132
+ },
133
+ "branch": {
134
+ "label": "需求分支",
135
+ "type": "text",
136
+ "value": "story/1020124"
137
+ },
138
+ "risks": {
139
+ "label": "当前风险",
140
+ "type": "list",
141
+ "value": ["等待服务端字段确认", "灰度策略待补充"]
142
+ },
143
+ "design": {
144
+ "label": "技术方案",
145
+ "type": "link",
146
+ "value": "打开方案文档",
147
+ "url": "https://docs.example.test/1020124"
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ 通用字段类型:
158
+
159
+ | `type` | `value` | 页面样式 |
160
+ | --- | --- | --- |
161
+ | `text` | 字符串、数字或可提取 label/name/value 的对象 | 普通文本,无胶囊背景 |
162
+ | `user` | 字符串或含 username/userId/name 的对象 | 负责人强调文本 |
163
+ | `chips` | 标量或数组 | 一个或多个标签胶囊 |
164
+ | `list` | 标量或数组 | 纵向项目符号列表 |
165
+ | `link` | 显示值,加 field 或 value 中的 `url/href` | 可点击文本和外链图标 |
166
+
167
+ section key 为 `progress` 时使用紧凑响应式网格;其他 section 默认纵向排列。空 value 不渲染,未知 type 回退为 `text`。
168
+
169
+ ## 4. GET /api/workflows/state
170
+
171
+ 读取当前物化快照。任何写入前都应先调用它,并保存 `snapshot.runtimeRevision`。
172
+
173
+ ### 4.1 请求
174
+
175
+ ```http
176
+ GET /api/workflows/state?workflow=tapd%3A1020124&runtimeOnly=1
177
+ Authorization: Bearer <AGENTFLOW_TOKEN>
178
+ ```
179
+
180
+ | Query 参数 | 类型 | 必填 | 含义 |
181
+ | --- | --- | --- | --- |
182
+ | `workflow` | string | 与 namespace/id 二选一 | 规范 key,例如 `tapd:1020124` |
183
+ | `namespace` | string | 与 workflow 二选一 | 当前仅支持 `tapd` |
184
+ | `id` | string | 与 workflow 二选一 | TAPD short ID |
185
+ | `runtimeOnly` | `0 \| 1` | 否 | `1` 只读取已保存运行态,不主动刷新上游;CLI 的 `--runtime-only` 使用它 |
186
+ | `flowId` | string | 否 | 关联 AgentFlow 项目时指定项目 ID |
187
+ | `flowSource` | string | 否 | 项目来源,默认 `user` |
188
+ | `workspaceId` | string | 否 | 项目工作区上下文 |
189
+ | `workflowShare` | string | 否 | 只读分享 token;不能用于写接口 |
190
+
191
+ ### 4.2 成功响应
192
+
193
+ ```json
194
+ {
195
+ "ok": true,
196
+ "workflow": { "namespace": "tapd", "id": "1020124", "key": "tapd:1020124" },
197
+ "snapshot": {
198
+ "runtimeRevision": "runtime:...",
199
+ "globalState": {},
200
+ "actions": [],
201
+ "artifacts": [],
202
+ "projections": { "timeline": [] },
203
+ "extensions": {}
204
+ }
205
+ }
206
+ ```
207
+
208
+ `snapshot` 只由服务端返回。客户端不得把一份旧 `snapshot` 原样 POST 回去。
209
+
210
+ ## 5. POST /api/workflows/report
211
+
212
+ 统一写入口。一次请求可以只更新一个区域,也可以原子地组合 Action、产物、全局事实、迭代归属和自定义区域。
213
+
214
+ ### 5.1 请求 Envelope
215
+
216
+ ```json
217
+ {
218
+ "schemaVersion": 1,
219
+ "workflow": { "namespace": "tapd", "id": "1020124" },
220
+ "source": "my-adapter",
221
+ "expectedRevision": "runtime:...",
222
+ "idempotencyKey": "implementation-finished:android:issue-1:v1",
223
+ "observation": {},
224
+ "action": {},
225
+ "artifacts": [],
226
+ "globalState": {},
227
+ "projections": {},
228
+ "extensions": {}
229
+ }
230
+ ```
231
+
232
+ | 顶层字段 | 类型 | 必填 | 含义 / 写入区域 |
233
+ | --- | --- | --- | --- |
234
+ | `schemaVersion` | number | 否 | 当前固定为 `1` |
235
+ | `workflow` | object/string | 是 | `{namespace,id}` 或规范 key;当前 namespace 仅支持 `tapd` |
236
+ | `source` | string | 强烈建议 | 小写稳定的业务 Adapter 名称;默认 `agentflow-cli` 仅用于兼容,不应作为正式接入的生产方身份 |
237
+ | `expectedRevision` | string | 修改已有状态时建议必填 | 最近一次 GET 返回的 runtime revision |
238
+ | `idempotencyKey` | string | 强烈建议 | 一次业务语义操作的稳定身份,不使用时间戳或随机 UUID |
239
+ | `observation` | object | 条件必填 | 同一 `clientId` 的完整生产方观察 |
240
+ | `action` | object | 条件必填 | 一条关键业务阶段 |
241
+ | `artifacts` | array | 条件必填 | Action 证据或全局证据 |
242
+ | `globalState` | object | 条件必填 | 生产方事实的 merge patch / remove |
243
+ | `projections` | object | 条件必填 | 通用迭代索引;当前包含完整 `timeline` 数组 |
244
+ | `extensions` | object | 条件必填 | 按生产方 namespace 组织的自定义区域数据 |
245
+ | `flowId` | string | 否 | 关联项目 ID |
246
+ | `flowSource` | string | 否 | 关联项目来源,默认 `user` |
247
+
248
+ `observation`、`action`、`artifacts`、`globalState`、`projections`、`extensions` 至少出现一个。
249
+
250
+ ### 5.2 成功响应
251
+
252
+ ```json
253
+ {
254
+ "ok": true,
255
+ "alreadyApplied": false,
256
+ "report": {},
257
+ "event": {},
258
+ "observation": { "accepted": true, "clientId": "my-adapter" },
259
+ "snapshot": { "runtimeRevision": "runtime:new-revision" }
260
+ }
261
+ ```
262
+
263
+ 没有 `observation` 时,响应中的 `observation` 为 `null`。同一 `source + idempotencyKey` 的幂等重放返回 `alreadyApplied: true`,应按成功处理;不同 source 可以安全复用相同业务 key。
264
+
265
+ ## 6. POST /api/workflow-artifacts/publish:发布 Markdown 预览
266
+
267
+ 把客户端本地 Markdown 内容保存成可访问的运行态副本,并返回预览链接。服务端不能读取客户端文件路径,所以必须发送 `markdown` 内容。
268
+
269
+ ### 6.1 请求
270
+
271
+ ```json
272
+ {
273
+ "workflow": { "namespace": "tapd", "id": "1020124" },
274
+ "source": "my-adapter",
275
+ "title": "Issue1 · Android 方案草稿",
276
+ "markdown": "# 方案内容\n...",
277
+ "stage": "issue-plan:runtime-hook",
278
+ "issueKey": "runtime-hook",
279
+ "platform": "android",
280
+ "artifactKey": "plan:runtime-hook:android",
281
+ "artifactLabel": "方案预览",
282
+ "durability": "temporary",
283
+ "ttlDays": 7,
284
+ "expectedRevision": "runtime:...",
285
+ "idempotencyKey": "review:plan:runtime-hook:android:<content-digest>"
286
+ }
287
+ ```
288
+
289
+ | 字段 | 类型 | 必填 | 含义 |
290
+ | --- | --- | --- | --- |
291
+ | `workflow` | object/string | 是 | 目标 Workflow |
292
+ | `source` | string | 强烈建议 | 真实业务 Adapter 的稳定名称;不是 `agentflow-cli` |
293
+ | `title` | string | 是 | Review 页面标题 |
294
+ | `markdown` | string | 是 | Markdown 实际内容,不是本地路径 |
295
+ | `stage` / `stageKey` | string | 建议 | 关联的稳定 Action 阶段 |
296
+ | `issueKey` | string | 否 | 自定义 Issue 身份 |
297
+ | `platform` | string | 否 | 平台维度 |
298
+ | `artifactKey` | string | 是 | 预览 Artifact 的稳定槽位 |
299
+ | `artifactLabel` | string | 否 | 页面按钮文案,默认 `Markdown Review` |
300
+ | `durability` | string | 否 | `temporary` 或 `durable`;默认临时 |
301
+ | `ttlDays` | number | 临时预览建议 | 临时副本有效天数,通常为 7 |
302
+ | `expectedRevision` | string | 修改已有状态时建议必填 | 最近一次 GET 返回的 runtime revision;过期返回 `409` |
303
+ | `idempotencyKey` | string | 强烈建议 | 建议包含内容摘要;同一 source + key 重放返回同一个预览,不创建新副本 |
304
+
305
+ ### 6.2 成功响应
306
+
307
+ 响应包含:
308
+
309
+ - `artifact`:可挂到页面的标准 Artifact。
310
+ - `review.url`:规范预览 URL。
311
+ - `review.shortUrl`:通常可直接分享的 `/r/<code>` 短链。
312
+ - `event`:辅助运行态事件,不推进业务阶段。
313
+ - `snapshot`:发布后的最新 Workflow 快照。
314
+
315
+ 发布预览不会确认方案、修改本地文件、提交 ai-doc、创建 GitLab Issue 或推进 Action。外部系统已经提供 HTTP URL 时,不需要调用本接口,直接在 `/api/workflows/report` 的 `artifacts` 中上报即可。
316
+
317
+ ## 7. 字段模型
318
+
319
+ ### 7.1 observation:完整生产方观察
320
+
321
+ ```json
322
+ {
323
+ "schema": "my-adapter/v1",
324
+ "clientId": "my-adapter-main",
325
+ "observedAt": "2026-08-05T10:00:00.000Z",
326
+ "scope": "client",
327
+ "state": { "phase": "implementing", "pointer": "Android 实现中" }
328
+ }
329
+ ```
330
+
331
+ | 字段 | 必填 | 含义 |
332
+ | --- | --- | --- |
333
+ | `schema` | 否 | 生产方状态 schema,默认 `workflow-observation/v1` |
334
+ | `clientId` | 建议 | 观察来源稳定身份;同一 clientId 的新观察替换旧观察 |
335
+ | `observedAt` | 建议 | ISO 时间 |
336
+ | `scope` | 否 | 默认 `client` |
337
+ | `state` | 是 | 完整观察对象,不是 patch |
338
+
339
+ ### 7.2 action:Action 时间轴节点
340
+
341
+ ```json
342
+ {
343
+ "key": "implementation:runtime-hook:android",
344
+ "title": "Android 实现完成",
345
+ "detail": "MR !957 已合并",
346
+ "status": "done",
347
+ "group": "implementation",
348
+ "scope": "runtime-hook",
349
+ "platform": "android",
350
+ "issueKey": "runtime-hook",
351
+ "tags": ["client"],
352
+ "occurredAt": "2026-08-05T08:00:00.000Z"
353
+ }
354
+ ```
355
+
356
+ | 字段 | 必填 | 含义 |
357
+ | --- | --- | --- |
358
+ | `key` | 是 | 稳定阶段身份;同 key 更新同一阶段 |
359
+ | `title` | 否 | 卡片标题,默认 key |
360
+ | `detail` | 否 | 阶段摘要,最长按服务端约束截断 |
361
+ | `status` | 否 | `pending/running/done/error/conflict/skipped/cancelled/observed` |
362
+ | `group` | 否 | 阶段分组,例如 `implementation` |
363
+ | `scope` | 否 | 业务范围 |
364
+ | `platform` | 否 | 平台维度 |
365
+ | `issueKey` | 否 | 自定义 Issue 身份 |
366
+ | `tags` | 否 | 字符串数组 |
367
+ | `occurredAt` | 否 | 业务发生时间;不要用重试时间覆盖它 |
368
+
369
+ `completed/success` 会规范化为 `done`,`failed` 会规范化为 `error`,未知状态回退为 `pending`。
370
+
371
+ ### 7.3 artifacts:Action 或全局证据
372
+
373
+ ```json
374
+ {
375
+ "key": "implementation-mr:runtime-hook:android",
376
+ "type": "gitlab-mr",
377
+ "title": "实现 MR !957",
378
+ "url": "https://git.example.test/merge_requests/957",
379
+ "scope": "action",
380
+ "status": "ready"
381
+ }
382
+ ```
383
+
384
+ | 字段 | 必填 | 含义 |
385
+ | --- | --- | --- |
386
+ | `key` | 强烈建议 | 稳定证据身份;不要依赖标题去重 |
387
+ | `type` | 否 | 产物类型,默认 `artifact` |
388
+ | `title` | 否 | 展示标题 |
389
+ | `url` / `path` | 至少一个 | 外部 URL 或可识别路径 |
390
+ | `scope` | 否 | `action` 或 `global`;有 Action 时默认 `action` |
391
+ | `status` | 否 | 生产方定义的证据状态 |
392
+
393
+ ### 7.4 globalState:全局事实 patch
394
+
395
+ ```json
396
+ {
397
+ "mode": "merge",
398
+ "patch": { "myProducer": { "owner": "alice", "platforms": ["android"] } },
399
+ "remove": ["myProducer.obsoleteField"]
400
+ }
401
+ ```
402
+
403
+ `mode` 当前只能是 `merge`。`patch` 与 `remove` 至少有一个。
404
+
405
+ ### 7.5 projections.timeline:迭代归属
406
+
407
+ ```json
408
+ {
409
+ "timeline": [{
410
+ "kind": "version",
411
+ "id": "android:1133202860001000338",
412
+ "key": "my-adapter:version:android:1133202860001000338",
413
+ "title": "Likee Android 5.63.0",
414
+ "date": "2026-08-31",
415
+ "source": "my-adapter",
416
+ "dimensions": { "platform": "android" },
417
+ "order": 0
418
+ }]
419
+ }
420
+ ```
421
+
422
+ | 字段 | 必填 | 含义 |
423
+ | --- | --- | --- |
424
+ | `kind` | 是 | `version`、`sprint`、`milestone` 等通用类型 |
425
+ | `id` | 是 | 生产方稳定身份;改名、改期时保持不变 |
426
+ | `key` | 否但建议 | 聚合身份;缺省时由 `source + kind + id` 推导 |
427
+ | `title` | 否 | 展示标题,默认 id |
428
+ | `date` | 否 | ISO 兼容日期,用于时间线排序 |
429
+ | `source` | 否 | 生产方 namespace |
430
+ | `dimensions` | 否 | 不透明筛选维度,例如 platform/team |
431
+ | `order` | 否 | 日期缺失或相同时的稳定顺序 |
432
+
433
+ 每次最多保留 100 条合法 timeline 项。
434
+
435
+ ### 7.6 extensions:自定义区域
436
+
437
+ ```json
438
+ {
439
+ "prd-flow": {
440
+ "aiDocs": [{ "key": "tech-design", "title": "技术方案", "url": "https://..." }],
441
+ "issues": [{ "key": "runtime-hook", "title": "Runtime Hook", "platform": "android" }]
442
+ }
443
+ }
444
+ ```
445
+
446
+ 扩展字段由生产方 schema 定义。AgentFlow 通用协议只校验 namespace 和顶层对象,不解释 `aiDocs`、`issues` 等私有字段。
447
+
448
+ ## 8. 覆盖、合并与删除规则
449
+
450
+ | 区域 | 省略字段 | 重复上报 | 删除 / 清空 |
451
+ | --- | --- | --- | --- |
452
+ | `observation.state` | 保持旧观察 | 同一 `clientId` 的完整 state 替换旧观察 | 上报生产方定义的空值结构;不要用它删除其他 client 的观察 |
453
+ | `globalState` | 不修改 | 对象递归 merge;数组和标量整体替换 | patch 中 `null` 删除字段;`remove` 在 patch 后删除 dot path |
454
+ | `action` | 不修改 Action | 同 `source + action.key` 更新同一业务阶段的可见状态 | 当前协议不提供物理删除 Action;用业务状态表达取消/跳过 |
455
+ | `artifacts` | 不修改产物 | 同 `source + stable key` 更新/归并同一可见证据 | 当前协议不提供通用物理删除;不要通过改 key 伪造删除 |
456
+ | `projections.timeline` | 不修改 | **整数组替换**,不是按 key merge | `[]` 清空全部迭代归属 |
457
+ | `extensions` | 不修改扩展 | namespace 内对象递归 merge;数组/标量替换 | 对应字段上报 `null` 删除;不要覆盖别人的 namespace |
458
+
459
+ 更新 `projections.timeline` 前必须先 GET,保留不属于当前生产方的条目,再替换当前生产方拥有的 key。服务端不会自动按 `source` 帮你合并。
460
+
461
+ ## 9. 并发、幂等与错误码
462
+
463
+ ### 9.1 安全写入顺序
464
+
465
+ 1. GET 当前 Workflow。
466
+ 2. 保存 `snapshot.runtimeRevision`。
467
+ 3. 基于最新快照计算语义 patch,以及完整 timeline 数组。
468
+ 4. POST 时带稳定 `source`、`expectedRevision` 与 `idempotencyKey`。
469
+ 5. 收到 `409` 后重新 GET、重新合并,只重试一次。
470
+
471
+ 不得在 revision 冲突后原样重放旧的完整数组。
472
+
473
+ ### 9.2 幂等键
474
+
475
+ 幂等键标识业务操作,不标识 HTTP 尝试:
476
+
477
+ ```text
478
+ <operation>:<scope>:<entity>:<semantic-version>
479
+ ```
480
+
481
+ 例如:
482
+
483
+ ```text
484
+ implementation-finished:android:runtime-hook:v1
485
+ timeline-membership:tapd-1020124:android-version-1133202860001000338:v1
486
+ ```
487
+
488
+ 同一 `source + idempotencyKey` 的 Workflow Report 或 Artifact Publish 重放返回 `alreadyApplied: true`。Publish 会返回第一次创建的预览,不会先生成一个新文件再去重。业务内容发生变化时提高语义版本或使用内容摘要;不要使用请求时间。
489
+
490
+ ### 9.3 错误码
491
+
492
+ | HTTP | 含义 | 处理方式 |
493
+ | --- | --- | --- |
494
+ | `400` | JSON、namespace、字段或 schema 不合法 | 按协议修正;不要降级校验 |
495
+ | `401` | 缺少或无效认证 | 停止并配置 Token;不要把 Token 打印出来 |
496
+ | `403` | 当前用户只有 viewer 权限或无权访问目标项目 | 停止;由 owner 授予 editor 或改用正确身份 |
497
+ | `404` | 分享链接、owner 或目标资源不存在 | 重新解析目标,不要创建影子副本 |
498
+ | `409` | runtime revision 已变化 | GET 最新状态、重新合并、重试一次 |
499
+ | `500` | 服务端异常 | 保留幂等键,记录脱敏上下文后重试或上报 |
500
+
501
+ ## 10. 三个关键接入场景
502
+
503
+ ### 10.1 更新迭代:绑定或切换版本
504
+
505
+ 1. 从业务系统取得稳定版本 ID、标题、日期和平台。
506
+ 2. GET 当前 Workflow。
507
+ 3. 用 `globalState.patch` 保存生产方拥有的完整版本事实。
508
+ 4. 从现有 timeline 中保留其他生产方条目,替换自己的 `kind=version` 条目。
509
+ 5. 使用最新 revision 上报。
510
+
511
+ 版本改名或改期时保持 `id/key` 不变,只改 `title/date`;切换版本时移除旧自有 key、加入新 key;取消归属时只移除自己的版本条目。
512
+
513
+ ### 10.2 上报 Action 与产物链接
514
+
515
+ 1. 先完成真实业务动作,例如创建 MR 或完成构建。
516
+ 2. 使用稳定 `action.key` 上报阶段结果。
517
+ 3. 已有 HTTP URL 的 MR、构建、测试报告直接放入同一 Report 的 `artifacts`。
518
+ 4. 本地 Markdown 先调用 Artifact Publish,取得 URL 后再作为阶段证据使用。
519
+ 5. 重复刷新同一阶段继续使用原 key;不要每次新建 Action。
520
+
521
+ ### 10.3 上报自定义区域
522
+
523
+ 1. 先判断 `globalState.sections` 的 `text/user/chips/list/link` 是否足够;足够时直接使用通用渲染器。
524
+ 2. 只有通用组件不能表达时,才定义稳定 namespace 和版本化扩展 schema。
525
+ 3. 把专用结构化事实放入 `extensions[namespace]`。
526
+ 4. 注册对应页面渲染器;否则数据只会被保存,不会自动出现专用 UI。当前只有 `extensions["prd-flow"]` 已注册。
527
+ 5. 更新数组时发送该数组的完整新值;更新对象字段时可以递归 merge;用 `null` 删除自有字段。
528
+
529
+ ## 11. prd-flow 参考映射
530
+
531
+ prd-flow 只是一个接入实现,不是协议依赖:
532
+
533
+ | prd-flow 事实 | 通用协议位置 | 页面结果 |
534
+ | --- | --- | --- |
535
+ | TAPD short ID | `workflow = tapd:<id>` | 串起同一需求、权限和分享 |
536
+ | 计算出的完整当前状态 | `observation.state` | Workflow 全局概览和当前指针 |
537
+ | TAPD 当前版本原始信息 | `globalState.tapdCurrentVersion` | 保留版本业务事实 |
538
+ | 由版本事实派生的归属 | `projections.timeline[kind=version]` | 个人/团队迭代时间线 |
539
+ | 方案确认、实现、提测、发布 | `action` | Action 时间轴和进度 |
540
+ | MR、Jenkins、测试报告 URL | `artifacts` | Action 下产物入口 |
541
+ | 本地方案 Markdown | Artifact Publish | 可分享方案预览 URL |
542
+ | AI Docs / Issues | `extensions["prd-flow"]` | prd-flow 专用文档区和 Issue 区 |
543
+
544
+ 旧的 `/api/prd-workflow/snapshot`、`/api/prd-workflow/event`、`/api/prd-workflow/review-link` 仅供旧客户端迁移,返回弃用提示。新接入不得使用。
545
+
546
+ ## 12. 验收清单
547
+
548
+ - 能使用 Token GET 当前 Workflow,并读到 runtime revision。
549
+ - 首次写入能建立 owner;editor 能写,viewer、团队成员、分享链接和管理员代看不能写。
550
+ - `globalState` 更新不会覆盖其他生产方路径,数组替换行为符合预期。
551
+ - 同一 Action key 重报不产生重复业务阶段。
552
+ - Action 下能看到稳定 key 的 MR、构建或测试产物。
553
+ - Markdown Publish 返回可访问 URL,但不会推进业务状态。
554
+ - 版本改名/改期不产生新迭代节点,版本切换不会删除第三方 Sprint。
555
+ - 自定义 extension 能保存;注册渲染器后能显示对应文档区 / Issue 区。
556
+ - 409 会触发一次 read → re-merge → retry。
557
+ - 幂等重放返回成功且不重复应用。
558
+ - Token 不出现在 JSON、日志、Artifact 或最终输出中。