@fieldwangai/agentflow 0.1.135 → 0.1.137

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.
@@ -1,337 +1,631 @@
1
- # AgentFlow Workflow Report Protocol
1
+ # AgentFlow Workflow Report 接入协议
2
2
 
3
- ## Contents
3
+ ## 目录
4
4
 
5
- 1. Contract boundary
6
- 2. Transport and authentication
7
- 3. Read endpoint
8
- 4. Report envelope
9
- 5. Action model
10
- 6. Artifact model
11
- 7. Global-state model
12
- 8. Timeline projection model
13
- 9. Concurrency and idempotency
14
- 10. Producer integration procedure
15
- 11. Examples
16
- 12. Acceptance checklist
5
+ 1. 接入边界
6
+ 2. 认证、身份与权限(含 POST /api/workflows/access/sync)
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
17
 
18
- ## 1. Contract boundary
18
+ ## 1. 接入边界
19
19
 
20
- AgentFlow owns transport, validation, event persistence, materialization, permissions, optimistic concurrency, idempotency, and dashboard aggregation.
20
+ 新接入使用三个运行态数据接口,以及一个独立的权限控制面接口:
21
21
 
22
- The producer owns the meaning and internal schema of `globalState`. AgentFlow must not parse private fields to infer version, sprint, release, or milestone membership.
22
+ | 方法 | 路径 | 用途 | 是否修改 Workflow |
23
+ | --- | --- | --- | --- |
24
+ | `GET` | `/api/workflows/state` | 读取当前快照和资源 key 版本 | 否 |
25
+ | `POST` | `/api/workflows/report` | 上报全局信息、Action、普通产物、迭代归属和自定义区域 | 是 |
26
+ | `POST` | `/api/workflow-artifacts/publish` | 把本地 Markdown 内容发布成浏览器可访问的预览链接 | 是 |
27
+ | `POST` | `/api/workflows/access/sync` | 同步 TAPD Owner 和参与人的派生权限 | 只修改权限 |
23
28
 
24
- The producer derives `projections` from its current state. Projections are replaceable indexes for generic AgentFlow views, not a second source of truth.
29
+ `agentflow-workflow-report` 是接入规格;`workflow-report-client.mjs` 是可复用客户端;`agentflow-cli` 是命令行包装;AgentFlow 服务才负责鉴权、存储、合并和展示。Skill 不参与运行时传输,CLI 也不是数据生产方。
25
30
 
26
- ## 2. Transport and authentication
31
+ 接入方负责采集业务系统事实并解释业务含义。例如 prd-flow 会读取 TAPD、ai-doc、GitLab 和 Jenkins;其他接入方可以读取完全不同的数据源。AgentFlow 不会替接入方修改这些上游系统。
27
32
 
28
- Default service URL:
33
+ 当前服务端仅支持 `tapd` Workflow namespace。稳定身份为 `tapd:<short-id>`,标题、版本名或阶段名都不能作为 Workflow 身份。这是“当前身份适配器的边界”,不是 Workflow Report 数据模型只能描述 TAPD;其他 namespace 要先扩展服务端身份、协作和存储适配器,不能只改请求字符串。
29
34
 
30
- ```text
31
- http://ai.mengma.bigo.inner/
35
+ ## 2. 认证、身份与权限
36
+
37
+ ### 2.1 认证
38
+
39
+ 使用 Bearer Token:
40
+
41
+ ```http
42
+ Authorization: Bearer <AGENTFLOW_TOKEN>
43
+ Content-Type: application/json
32
44
  ```
33
45
 
34
- Use bearer authentication through `AGENTFLOW_TOKEN` or `AGENTFLOW_SESSION_TOKEN`. For local testing only, set `AGENTFLOW_BASE_URL` to the local server URL.
46
+ CLI `AGENTFLOW_TOKEN` `AGENTFLOW_SESSION_TOKEN` 读取凭证。不得把 Token 放入请求 JSON、Action、Artifact、日志或代码仓库。
47
+
48
+ ### 2.2 权限
49
+
50
+ | 身份 | 读取 | 上报 / 发布预览 | 管理成员与分享 |
51
+ | --- | --- | --- | --- |
52
+ | TAPD Owner / Workflow Owner | 是 | 是 | 是 |
53
+ | 显式 Reporter | 是 | 是 | 否 |
54
+ | TAPD 参与人 | 是 | 否 | 否 |
55
+ | 显式 Viewer | 是 | 否 | 否 |
56
+ | owner 同团队成员 | 是,团队视图自动获得 viewer 权限 | 否 | 否 |
57
+ | 分享链接访问者 | 是 | 否 | 否 |
58
+ | 超级管理员代看 | 是 | 否,只读审阅 | 否 |
59
+
60
+ 完成权限同步后,TAPD 需求 Owner 就是 Workflow Owner。TAPD 参与人匹配到已注册的 AgentFlow 账号后,默认得到派生 Viewer,不会自动获得上报权限。Owner 可在 AgentFlow 中显式授予 Reporter 或 Viewer。
61
+
62
+ 派生权限和显式授权分开保存:后续 TAPD 刷新可以增加或移除派生 Viewer,但不能抹掉 Owner 主动给出的显式授权。尚未同步 TAPD 人员的历史 Workflow 保留已有 Owner,避免升级时突然撤销权限。兼容客户端若跳过 access sync,首次上报仍会建立 `legacy` Owner;新接入不得依赖这个回退,应先同步 TAPD 权限。旧角色字符串 `editor` 作为兼容别名继续接受,并统一物化为 `reporter`。没有写权限的调用返回 `403`,不会回退成调用者自己的副本。
35
63
 
36
- Preferred transport is the bundled CLI because it resolves env files and auth headers without exposing tokens. Direct HTTP integrations may call the endpoints below with `Authorization: Bearer <token>` and `Content-Type: application/json`.
64
+ ### 2.3 TAPD 权限同步
37
65
 
38
- ## 3. Read endpoint
66
+ Adapter 读取 TAPD Story 后、上报运行态之前,调用 `POST /api/workflows/access/sync`:
39
67
 
40
- CLI:
68
+ ```json
69
+ {
70
+ "workflow": { "namespace": "tapd", "id": "1020124" },
71
+ "authority": {
72
+ "type": "tapd",
73
+ "owner": { "username": "alice" },
74
+ "participants": ["alice", "bob", "carol"],
75
+ "observedAt": "2026-08-05T08:00:00.000Z",
76
+ "revision": "tapd-story-modified-at-or-content-digest"
77
+ }
78
+ }
79
+ ```
80
+
81
+ | 字段 | 必填 | 含义 |
82
+ | --- | --- | --- |
83
+ | `workflow` | 是 | 规范身份;当前仅支持 `tapd:<short-id>` |
84
+ | `authority.type` | 是 | 当前固定为 `tapd` |
85
+ | `authority.owner` | 是 | TAPD Owner username/userId;必须已注册或登录过 AgentFlow |
86
+ | `authority.participants` | 否 | TAPD 参与人 username/userId;匹配后成为派生 Viewer |
87
+ | `authority.observedAt` | 建议 | 读取人员快照的时间;旧于已保存快照时返回 `409` |
88
+ | `authority.revision` | 建议 | TAPD `modified` 值或人员内容摘要,用于审计和排查 |
89
+
90
+ 新 Workflow 首次同步时,当前登录用户必须是映射后的 TAPD Owner;超级管理员可代为初始化。后续同步只允许当前 Workflow Owner 或超级管理员执行。Owner 发生变化时只更新管理身份,Workflow 使用稳定的内部状态空间,已有 Action、产物和全局信息不会搬迁或变空。未注册的参与人会出现在响应的 `unresolvedParticipants` 中;他们注册并在后续同步被匹配前不获得权限。
91
+
92
+ CLI 等价命令:
41
93
 
42
94
  ```bash
43
- node skills/agentflow-cli/scripts/agentflow-cli.mjs workflow-get \
44
- --workflow tapd:1015046 \
45
- --runtime-only
95
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs workflow-access-sync \
96
+ --workflow tapd:1020124 \
97
+ --file workflow-access.json
46
98
  ```
47
99
 
48
- HTTP:
100
+ ## 3. 数据区域模型
101
+
102
+ Workflow 页面由三类数据区域组成:
103
+
104
+ ### 3.1 全局区域
105
+
106
+ 描述“这个需求现在是什么”:标题、状态、负责人、平台、当前分支、研发进度、版本原始事实等。
107
+
108
+ - 完整的生产方观察放在 `observation.state`。
109
+ - 可独立增量更新的生产方事实放在 `globalState`。
110
+ - 个人/团队迭代所需的版本、Sprint、里程碑归属放在 `projections.timeline`。
111
+
112
+ `globalState` 是生产方拥有的事实;`projections` 是可从事实重建的通用索引,不能反过来作为业务真相。
113
+
114
+ ### 3.2 Action 时间轴
115
+
116
+ 描述“关键阶段发生了什么”:方案确认、开始实现、MR 创建、提测、发布完成等。
117
+
118
+ - 阶段本身使用 `action`。
119
+ - MR、构建、测试报告、外部文档链接使用 `artifacts`。
120
+ - `action.key` 是稳定阶段身份;同一个 key 的重复上报更新同一阶段,而不是制造一条新业务阶段。
121
+
122
+ Action 是业务节点,不是运行日志。轮询、刷新、重试等技术动作不应各自创建 Action。
123
+
124
+ ### 3.3 自定义区域
125
+
126
+ 描述只有某个接入实现才理解的结构化面板,例如 prd-flow 的 AI Docs 和 Issues。
127
+
128
+ - 数据放入 `extensions["<producer-namespace>"]`。
129
+ - namespace 必须为小写稳定标识,例如 `prd-flow`。
130
+ - AgentFlow 对未知扩展按不透明 JSON 保存;只有注册了渲染器的 namespace 才会显示成专用面板。
131
+
132
+ AI Docs / Issues 不是通用固定字段。当前唯一注册的 extension renderer 是 `prd-flow`,它识别 AI Docs 链接列表和带父子层级、平台、MR 状态及关联链接的 Issues 树。其他 namespace 会被保存并参与 revision,但不会自动出现页面。
133
+
134
+ 普通的负责人、平台、分支、风险列表和文档链接不需要 extension。优先使用下面的 `globalState.sections` 通用渲染器;只有现有组件无法表达的树形结构、复杂交互或专用业务面板,才定义新的 extension schema 和前端 renderer。
135
+
136
+ ### 3.4 当前可直接使用的通用渲染器
137
+
138
+ | 页面组件 | 上报字段 | 展示样式 | 是否需要前端开发 |
139
+ | --- | --- | --- | --- |
140
+ | 需求概览 | `globalState.title/url/status` | 标题、外链和状态标签 | 否 |
141
+ | 自定义概览分区 | `globalState.sections` | 分区卡片与固定字段样式 | 否 |
142
+ | Action 时间轴 | `action` | 按日期分组的状态点、时间、标题、详情和维度标签 | 否 |
143
+ | Action 产物 | `artifacts[scope=action]` | Action 下的链接按钮 | 否 |
144
+ | 关联产物 | `artifacts[scope=global]` | 侧栏链接列表,展示标题和产物类型 | 否 |
145
+ | 迭代时间线 | `projections.timeline` | 版本/Sprint/里程碑时间线卡片与筛选 | 否 |
146
+ | prd-flow AI Docs / Issues | `extensions["prd-flow"]` | 文档链接列表、层级 Issue 卡片 | 已注册,仅供 prd-flow schema |
147
+ | 其他专用面板 | `extensions["<namespace>"]` | 由接入方设计 | 是,需要注册 schema、空态/错误态、响应式样式和 renderer |
148
+
149
+ `globalState.sections` schema:
150
+
151
+ ```json
152
+ {
153
+ "globalState": {
154
+ "mode": "merge",
155
+ "patch": {
156
+ "title": "Remote Config 拉取频控",
157
+ "url": "https://tapd.example.test/1020124",
158
+ "status": "实现中",
159
+ "sections": {
160
+ "ownership": {
161
+ "title": "归属信息",
162
+ "fields": {
163
+ "owner": {
164
+ "label": "负责人",
165
+ "type": "user",
166
+ "value": { "username": "alice" }
167
+ },
168
+ "platforms": {
169
+ "label": "平台",
170
+ "type": "chips",
171
+ "value": ["Android", "iOS"]
172
+ },
173
+ "branch": {
174
+ "label": "需求分支",
175
+ "type": "text",
176
+ "value": "story/1020124"
177
+ },
178
+ "risks": {
179
+ "label": "当前风险",
180
+ "type": "list",
181
+ "value": ["等待服务端字段确认", "灰度策略待补充"]
182
+ },
183
+ "design": {
184
+ "label": "技术方案",
185
+ "type": "link",
186
+ "value": "打开方案文档",
187
+ "url": "https://docs.example.test/1020124"
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ 通用字段类型:
198
+
199
+ | `type` | `value` | 页面样式 |
200
+ | --- | --- | --- |
201
+ | `text` | 字符串、数字或可提取 label/name/value 的对象 | 普通文本,无胶囊背景 |
202
+ | `user` | 字符串或含 username/userId/name 的对象 | 负责人强调文本 |
203
+ | `chips` | 标量或数组 | 一个或多个标签胶囊 |
204
+ | `list` | 标量或数组 | 纵向项目符号列表 |
205
+ | `link` | 显示值,加 field 或 value 中的 `url/href` | 可点击文本和外链图标 |
206
+
207
+ section key 为 `progress` 时使用紧凑响应式网格;其他 section 默认纵向排列。空 value 不渲染,未知 type 回退为 `text`。
208
+
209
+ ### 3.5 资源 key
210
+
211
+ 并发冲突与所有权都落在稳定资源 key,而不是整份 JSON:
212
+
213
+ ```text
214
+ action:<source>:<action.key>
215
+ artifact:<source>:<artifact.key>
216
+ projection:<source>:<kind>:<id>
217
+ global:<dot.path>
218
+ extension:<source>:<dot.path>
219
+ observation:<source>:<clientId>
220
+ ```
221
+
222
+ 同一请求可以触及多个 key。服务端在 Workflow 写锁内一次性校验全部 key;任意一个 key 冲突时整次请求不落库。`resourceKeys` 会随成功响应返回,便于接入方记录实际写入边界。
223
+
224
+ ## 4. GET /api/workflows/state
225
+
226
+ 读取当前物化快照。任何写入前都应先调用它,并保存本次会触及 key 对应的 `snapshot.resourceVersions`。
227
+
228
+ ### 4.1 请求
49
229
 
50
230
  ```http
51
- GET /api/workflows/state?workflow=tapd%3A1015046&runtimeOnly=1
231
+ GET /api/workflows/state?workflow=tapd%3A1020124&runtimeOnly=1
232
+ Authorization: Bearer <AGENTFLOW_TOKEN>
52
233
  ```
53
234
 
54
- Use the returned `snapshot.runtimeRevision` as `expectedRevision`. Read the existing `snapshot.globalState` before producing a patch and the existing `snapshot.projections.timeline` before replacing timeline membership.
235
+ | Query 参数 | 类型 | 必填 | 含义 |
236
+ | --- | --- | --- | --- |
237
+ | `workflow` | string | 与 namespace/id 二选一 | 规范 key,例如 `tapd:1020124` |
238
+ | `namespace` | string | 与 workflow 二选一 | 当前仅支持 `tapd` |
239
+ | `id` | string | 与 workflow 二选一 | TAPD short ID |
240
+ | `runtimeOnly` | `0 \| 1` | 否 | `1` 只读取已保存运行态,不主动刷新上游;CLI 的 `--runtime-only` 使用它 |
241
+ | `flowId` | string | 否 | 关联 AgentFlow 项目时指定项目 ID |
242
+ | `flowSource` | string | 否 | 项目来源,默认 `user` |
243
+ | `workspaceId` | string | 否 | 项目工作区上下文 |
244
+ | `workflowShare` | string | 否 | 只读分享 token;不能用于写接口 |
55
245
 
56
- The deployed server currently supports the `tapd` Workflow namespace. The reference object remains namespaced for future producers:
246
+ ### 4.2 成功响应
57
247
 
58
248
  ```json
59
249
  {
60
- "namespace": "tapd",
61
- "id": "1015046"
250
+ "ok": true,
251
+ "workflow": { "namespace": "tapd", "id": "1020124", "key": "tapd:1020124" },
252
+ "snapshot": {
253
+ "runtimeRevision": "runtime:...",
254
+ "resourceVersions": {
255
+ "action:my-adapter:implementation:issue-1": "rv:...",
256
+ "projection:my-adapter:version:android-123": "rv:..."
257
+ },
258
+ "globalState": {},
259
+ "actions": [],
260
+ "artifacts": [],
261
+ "projections": { "timeline": [] },
262
+ "extensions": {}
263
+ }
62
264
  }
63
265
  ```
64
266
 
65
- ## 4. Report envelope
267
+ `snapshot` 只由服务端返回。`runtimeRevision` 用于页面缓存和旧客户端的整 Workflow 严格锁;新接入使用 `resourceVersions` 做 key 级并发控制。客户端不得把一份旧 `snapshot` 原样 POST 回去。
66
268
 
67
- Endpoint:
269
+ ## 5. POST /api/workflows/report
68
270
 
69
- ```http
70
- POST /api/workflows/report
271
+ 统一写入口。一次请求可以只更新一个区域,也可以原子地组合 Action、产物、全局事实、迭代归属和自定义区域。
272
+
273
+ ### 5.1 请求 Envelope
274
+
275
+ ```json
276
+ {
277
+ "schemaVersion": 1,
278
+ "workflow": { "namespace": "tapd", "id": "1020124" },
279
+ "source": "my-adapter",
280
+ "expectedVersions": {
281
+ "action:my-adapter:implementation:android:issue-1": "rv:..."
282
+ },
283
+ "idempotencyKey": "implementation-finished:android:issue-1:v1",
284
+ "observation": {},
285
+ "action": {},
286
+ "artifacts": [],
287
+ "globalState": {},
288
+ "projections": {},
289
+ "extensions": {}
290
+ }
291
+ ```
292
+
293
+ | 顶层字段 | 类型 | 必填 | 含义 / 写入区域 |
294
+ | --- | --- | --- | --- |
295
+ | `schemaVersion` | number | 否 | 当前固定为 `1` |
296
+ | `workflow` | object/string | 是 | `{namespace,id}` 或规范 key;当前 namespace 仅支持 `tapd` |
297
+ | `source` | string | 是 | 小写稳定的业务 Adapter 名称;`agentflow-cli` 只是传输工具,不能作为默认生产方身份 |
298
+ | `expectedVersions` | object | 修改已有资源时建议必填 | 本次触及的全部资源 key 及 GET 返回的版本;创建新 key 使用 `absent` |
299
+ | `expectedRevision` | string | 兼容字段 | 仅在没有 `expectedVersions` 时启用的整 Workflow 严格锁;新接入不要使用 |
300
+ | `idempotencyKey` | string | 强烈建议 | 一次业务语义操作的稳定身份,不使用时间戳或随机 UUID |
301
+ | `observation` | object | 条件必填 | 同一 `clientId` 的完整生产方观察 |
302
+ | `action` | object | 条件必填 | 一条关键业务阶段 |
303
+ | `artifacts` | array | 条件必填 | Action 证据或全局证据 |
304
+ | `globalState` | object | 条件必填 | 生产方事实的 merge patch / remove |
305
+ | `projections` | object | 条件必填 | 通用迭代索引;提交当前 source 的完整 `timeline` 切片 |
306
+ | `extensions` | object | 条件必填 | 按生产方 namespace 组织的自定义区域数据 |
307
+ | `flowId` | string | 否 | 关联项目 ID |
308
+ | `flowSource` | string | 否 | 关联项目来源,默认 `user` |
309
+
310
+ `observation`、`action`、`artifacts`、`globalState`、`projections`、`extensions` 至少出现一个。
311
+
312
+ ### 5.2 成功响应
313
+
314
+ ```json
315
+ {
316
+ "ok": true,
317
+ "alreadyApplied": false,
318
+ "report": {},
319
+ "resourceKeys": ["action:my-adapter:implementation:android:issue-1"],
320
+ "event": {},
321
+ "observation": { "accepted": true, "clientId": "my-adapter" },
322
+ "snapshot": {
323
+ "runtimeRevision": "runtime:new-revision",
324
+ "resourceVersions": {
325
+ "action:my-adapter:implementation:android:issue-1": "rv:new-resource-version"
326
+ }
327
+ }
328
+ }
329
+ ```
330
+
331
+ 没有 `observation` 时,响应中的 `observation` 为 `null`。同一 `workflow + source + operation + idempotencyKey` 的幂等重放返回 `alreadyApplied: true`,应按成功处理;Report 与 Artifact Publish 使用独立操作域。
332
+
333
+ ## 6. POST /api/workflow-artifacts/publish:发布 Markdown 预览
334
+
335
+ 把客户端本地 Markdown 内容保存成可访问的运行态副本,并返回预览链接。服务端不能读取客户端文件路径,所以必须发送 `markdown` 内容。
336
+
337
+ ### 6.1 请求
338
+
339
+ ```json
340
+ {
341
+ "workflow": { "namespace": "tapd", "id": "1020124" },
342
+ "source": "my-adapter",
343
+ "title": "Issue1 · Android 方案草稿",
344
+ "markdown": "# 方案内容\n...",
345
+ "stage": "issue-plan:runtime-hook",
346
+ "issueKey": "runtime-hook",
347
+ "platform": "android",
348
+ "artifactKey": "plan:runtime-hook:android",
349
+ "artifactLabel": "方案预览",
350
+ "durability": "temporary",
351
+ "ttlDays": 7,
352
+ "expectedVersions": { "artifact:my-adapter:plan:runtime-hook:android": "absent" },
353
+ "idempotencyKey": "review:plan:runtime-hook:android:<content-digest>"
354
+ }
71
355
  ```
72
356
 
73
- Top-level fields:
357
+ | 字段 | 类型 | 必填 | 含义 |
358
+ | --- | --- | --- | --- |
359
+ | `workflow` | object/string | 是 | 目标 Workflow |
360
+ | `source` | string | 是 | 真实业务 Adapter 的稳定名称;不是 `agentflow-cli` |
361
+ | `title` | string | 是 | Review 页面标题 |
362
+ | `markdown` | string | 是 | Markdown 实际内容,不是本地路径 |
363
+ | `stage` / `stageKey` | string | 建议 | 关联的稳定 Action 阶段 |
364
+ | `issueKey` | string | 否 | 自定义 Issue 身份 |
365
+ | `platform` | string | 否 | 平台维度 |
366
+ | `artifactKey` | string | 是 | 预览 Artifact 的稳定槽位 |
367
+ | `artifactLabel` | string | 否 | 页面按钮文案,默认 `Markdown Review` |
368
+ | `durability` | string | 否 | `temporary` 或 `durable`;默认临时 |
369
+ | `ttlDays` | number | 临时预览建议 | 1–30 的整数,通常为 7 |
370
+ | `expectedVersions` | object | 修改已有 Artifact 时建议必填 | 只需包含目标 `artifact:source:key`;创建时使用 `absent` |
371
+ | `expectedRevision` | string | 兼容字段 | 仅在没有 `expectedVersions` 时使用整 Workflow 严格锁 |
372
+ | `idempotencyKey` | string | 强烈建议 | 建议包含内容摘要;同一 source + key 重放返回同一个预览,不创建新副本 |
373
+
374
+ ### 6.2 成功响应
375
+
376
+ 响应包含:
377
+
378
+ - `artifact`:可挂到页面的标准 Artifact。
379
+ - `review.url`:规范预览 URL。
380
+ - `review.shortUrl`:通常可直接分享的 `/r/<code>` 短链。
381
+ - `event`:辅助运行态事件,不推进业务阶段。
382
+ - `snapshot`:发布后的最新 Workflow 快照。
383
+
384
+ 发布预览不会确认方案、修改本地文件、提交 ai-doc、创建 GitLab Issue 或推进 Action。Markdown 最大 500,000 bytes;`durability` 只能是 `temporary/durable`。外部系统已经提供 HTTP URL 时,不需要调用本接口,直接在 `/api/workflows/report` 的 `artifacts` 中上报即可。
74
385
 
75
- | Field | Required | Meaning |
386
+ ## 7. 字段模型
387
+
388
+ ### 7.1 observation:完整生产方观察
389
+
390
+ ```json
391
+ {
392
+ "schema": "my-adapter/v1",
393
+ "clientId": "my-adapter-main",
394
+ "observedAt": "2026-08-05T10:00:00.000Z",
395
+ "scope": "client",
396
+ "state": { "phase": "implementing", "pointer": "Android 实现中" }
397
+ }
398
+ ```
399
+
400
+ | 字段 | 必填 | 含义 |
76
401
  | --- | --- | --- |
77
- | `schemaVersion` | No | Protocol version; defaults to `1` |
78
- | `workflow` | Yes | `{ namespace, id }` or canonical key |
79
- | `action` | Conditional | One lifecycle/progress update |
80
- | `artifacts` | Conditional | Evidence associated with the action or global state |
81
- | `globalState` | Conditional | Producer-owned merge patch and removals |
82
- | `projections` | Conditional | Generic replaceable dashboard indexes |
83
- | `expectedRevision` | For mutations | Revision returned by the latest read |
84
- | `idempotencyKey` | Recommended | Stable identity of the logical operation |
85
- | `source` | No | Reporting producer, default `agentflow-cli` |
86
- | `flowId` | No | Related AgentFlow project identifier |
87
- | `flowSource` | No | Related project source, default `user` |
88
-
89
- Include at least one of `action`, `artifacts`, `globalState`, or `projections`.
90
-
91
- ## 5. Action model
402
+ | `schema` | | 生产方状态 schema,默认 `workflow-observation/v1` |
403
+ | `clientId` | 建议 | 观察来源稳定身份;同一 clientId 的新观察替换旧观察 |
404
+ | `observedAt` | 建议 | ISO 时间 |
405
+ | `scope` | | 默认 `client` |
406
+ | `state` | | 完整观察对象,不是 patch |
407
+
408
+ ### 7.2 action:Action 时间轴节点
92
409
 
93
410
  ```json
94
411
  {
95
- "key": "implementation:android:issue-2",
412
+ "key": "implementation:runtime-hook:android",
96
413
  "title": "Android 实现完成",
97
- "detail": "Remote Config 拉取频控已实现",
414
+ "detail": "MR !957 已合并",
98
415
  "status": "done",
99
416
  "group": "implementation",
100
- "scope": "remote-config-android",
417
+ "scope": "runtime-hook",
101
418
  "platform": "android",
102
- "issueKey": "issue-2",
103
- "tags": ["remote-config"],
104
- "occurredAt": "2026-08-04T08:00:00.000Z"
419
+ "issueKey": "runtime-hook",
420
+ "tags": ["client"],
421
+ "occurredAt": "2026-08-05T08:00:00.000Z"
105
422
  }
106
423
  ```
107
424
 
108
- `key` is required and stable. Supported normalized statuses are `pending`, `running`, `done`, `error`, `conflict`, `skipped`, `cancelled`, and `observed`. Common aliases such as `completed` and `success` normalize to `done`.
425
+ | 字段 | 必填 | 含义 |
426
+ | --- | --- | --- |
427
+ | `key` | 是 | 稳定阶段身份;同 key 更新同一阶段 |
428
+ | `title` | 否 | 卡片标题,默认 key |
429
+ | `detail` | 否 | 阶段摘要,最多 4,000 字符;超限返回 `400`,不会截断 |
430
+ | `status` | 否 | `pending/running/done/error/conflict/skipped/cancelled/observed` |
431
+ | `group` | 否 | 阶段分组,例如 `implementation` |
432
+ | `scope` | 否 | 业务范围 |
433
+ | `platform` | 否 | 平台维度 |
434
+ | `issueKey` | 否 | 自定义 Issue 身份 |
435
+ | `tags` | 否 | 字符串数组 |
436
+ | `occurredAt` | 否 | 业务发生时间;不要用重试时间覆盖它 |
109
437
 
110
- Repeated reports for the same stage may update its visible timeline entry. Use a new action key only for a semantically different action.
438
+ `completed/success` 会规范化为 `done`,`failed` 会规范化为 `error`;未知状态返回 `400`,不会静默回退。
111
439
 
112
- ## 6. Artifact model
440
+ ### 7.3 artifacts:Action 或全局证据
113
441
 
114
442
  ```json
115
443
  {
116
- "key": "implementation-mr:issue-2:android",
444
+ "key": "implementation-mr:runtime-hook:android",
117
445
  "type": "gitlab-mr",
118
- "title": "Android 实现 MR",
119
- "url": "https://git.example.test/group/project/-/merge_requests/123",
446
+ "title": "实现 MR !957",
447
+ "url": "https://git.example.test/merge_requests/957",
120
448
  "scope": "action",
121
449
  "status": "ready"
122
450
  }
123
451
  ```
124
452
 
125
- Use stable keys. `scope` is `action` or `global`. Action-scoped artifacts appear with an action; global artifacts appear in the related-artifacts area. URL and path aliases may be deduplicated, but producers must not rely on title-based identity.
126
-
127
- ## 7. Global-state model
453
+ | 字段 | 必填 | 含义 |
454
+ | --- | --- | --- |
455
+ | `key` | 强烈建议 | 稳定证据身份;不要依赖标题去重 |
456
+ | `type` | 否 | 产物类型,默认 `artifact` |
457
+ | `title` | 否 | 展示标题 |
458
+ | `url` / `path` | 至少一个 | URL 只允许 `http/https` 或站内绝对路径;本地文件必须先 Publish,不能直接形成可访问链接 |
459
+ | `scope` | 否 | `action` 或 `global`;有 Action 时默认 `action` |
460
+ | `status` | 否 | 生产方定义的证据状态 |
128
461
 
129
- AgentFlow defines only the update operation:
462
+ ### 7.4 globalState:全局事实 patch
130
463
 
131
464
  ```json
132
465
  {
133
466
  "mode": "merge",
134
- "patch": {
135
- "producerDefined": {
136
- "anySafeJsonShape": true
137
- }
138
- },
139
- "remove": ["obsolete.path"]
467
+ "patch": { "myProducer": { "owner": "alice", "platforms": ["android"] } },
468
+ "remove": ["myProducer.obsoleteField"]
140
469
  }
141
470
  ```
142
471
 
143
- Rules:
472
+ `mode` 当前只能是 `merge`。`patch` 与 `remove` 至少有一个。
144
473
 
145
- - `mode` must be `merge`.
146
- - `patch` recursively merges objects; arrays and scalar values replace the existing value.
147
- - `null` removes a field during merge.
148
- - `remove` contains dot-separated paths and is applied after the patch.
149
- - Never send the entire state unless the producer intentionally owns and has reconciled every field.
150
- - AgentFlow does not require Android/iOS sections or any prd-flow-specific layout.
474
+ ### 7.5 projections.timeline:迭代归属
151
475
 
152
- ## 8. Timeline projection model
476
+ ```json
477
+ {
478
+ "timeline": [{
479
+ "kind": "version",
480
+ "id": "android:1133202860001000338",
481
+ "key": "my-adapter:version:android:1133202860001000338",
482
+ "title": "Likee Android 5.63.0",
483
+ "date": "2026-08-31",
484
+ "source": "my-adapter",
485
+ "dimensions": { "platform": "android" },
486
+ "order": 0
487
+ }]
488
+ }
489
+ ```
153
490
 
154
- Timeline projections give generic personal and team dashboards enough metadata to group Workflows without reading producer state:
491
+ | 字段 | 必填 | 含义 |
492
+ | --- | --- | --- |
493
+ | `kind` | 是 | `version`、`sprint`、`milestone` 等通用类型 |
494
+ | `id` | 是 | 生产方稳定身份;改名、改期时保持不变 |
495
+ | `key` | 否但建议 | 聚合身份;缺省时由 `source + kind + id` 推导 |
496
+ | `title` | 否 | 展示标题,默认 id |
497
+ | `date` | 否 | ISO 兼容日期,用于时间线排序 |
498
+ | `source` | 否 | 生产方 namespace |
499
+ | `dimensions` | 否 | 不透明筛选维度,例如 platform/team |
500
+ | `order` | 否 | 日期缺失或相同时的稳定顺序 |
501
+
502
+ 每个 source 最多上报 100 条合法 timeline 项;超过限制返回错误,不会截断。
503
+
504
+ ### 7.6 extensions:自定义区域
155
505
 
156
506
  ```json
157
507
  {
158
- "timeline": [
159
- {
160
- "kind": "version",
161
- "id": "android-5.63.0",
162
- "title": "Likee Android 5.63.0",
163
- "date": "2026-08-20",
164
- "source": "prd-flow",
165
- "dimensions": {
166
- "platform": "android"
167
- },
168
- "order": 0
169
- }
170
- ]
508
+ "prd-flow": {
509
+ "aiDocs": [{ "key": "tech-design", "title": "技术方案", "url": "https://..." }],
510
+ "issues": [{ "key": "runtime-hook", "title": "Runtime Hook", "platform": "android" }]
511
+ }
171
512
  }
172
513
  ```
173
514
 
174
- Fields:
515
+ 扩展字段由生产方 schema 定义。一次请求只能更新 `extensions[source]`;AgentFlow 通用协议不解释 `aiDocs`、`issues` 等私有字段。
175
516
 
176
- | Field | Required | Meaning |
177
- | --- | --- | --- |
178
- | `kind` | Yes | Generic membership type, such as `version`, `release`, `sprint`, or `milestone` |
179
- | `id` | Yes | Stable producer identity within the kind |
180
- | `title` | No | Display title; defaults to `id` |
181
- | `date` | No | ISO-compatible target date used for timeline sorting |
182
- | `source` | No | Producer namespace, such as `prd-flow` |
183
- | `dimensions` | No | Opaque grouping and filtering facets |
184
- | `order` | No | Stable fallback ordering when dates are absent or equal |
185
- | `key` | No | Explicit aggregate key; otherwise derived from `source`, `kind`, and `id` |
517
+ ## 8. 覆盖、合并与删除规则
186
518
 
187
- Replacement semantics:
519
+ | 区域 | 省略字段 | 重复上报 | 删除 / 清空 |
520
+ | --- | --- | --- | --- |
521
+ | `observation.state` | 保持旧观察 | 同一 `clientId` 的完整 state 替换旧观察 | 上报生产方定义的空值结构;不要用它删除其他 client 的观察 |
522
+ | `globalState` | 不修改 | 对象递归 merge;数组和标量整体替换;首次写入路径的 source 获得该路径所有权 | patch 中 `null` 删除字段;`remove` 在 patch 后删除 dot path;其他 source 不能改写已归属路径 |
523
+ | `action` | 不修改 Action | 同 `source + action.key` 更新同一业务阶段的可见状态 | 当前协议不提供物理删除 Action;用业务状态表达取消/跳过 |
524
+ | `artifacts` | 不修改产物 | 同 `source + stable key` 更新/归并同一可见证据 | 当前协议不提供通用物理删除;不要通过改 key 伪造删除 |
525
+ | `projections.timeline` | 不修改 | 替换当前 `source` 拥有的完整切片,服务端原子保留其他 source | `[]` 只清空当前 source 的迭代归属 |
526
+ | `extensions` | 不修改扩展 | 只允许 `extensions[source]` 内对象递归 merge;数组/标量替换 | 对应字段上报 `null` 删除 |
188
527
 
189
- - When `projections.timeline` is present, it is the complete current timeline membership and replaces the previous array.
190
- - `"timeline": []` explicitly clears all membership.
191
- - Omitting `projections` leaves the previous projection unchanged.
192
- - Multiple entries allow one Workflow to belong to multiple generic timelines.
193
- - Unknown `kind` and `dimensions` values remain valid and opaque.
528
+ 客户端可以在兼容 payload 中携带未修改的其他 source 条目,但服务端只接受完全一致的副本且不会使用它覆盖现状。推荐只发送当前 source 的完整切片,由服务端按 `source + kind + id` 合并。
194
529
 
195
- Dashboard aggregation uses the explicit `key` when supplied; otherwise it derives one from `source + kind + id`. Invalid entries without `kind` or `id` are rejected on report.
530
+ ## 9. 并发、幂等与错误码
196
531
 
197
- ## 9. Concurrency and idempotency
532
+ ### 9.1 安全写入顺序
198
533
 
199
- Use optimistic concurrency for every state or projection mutation:
534
+ 1. GET 当前 Workflow。
535
+ 2. 根据本次业务操作计算会触及的 `resourceKeys`。
536
+ 3. 从 `snapshot.resourceVersions` 复制这些 key 的版本;不存在的 key 使用 `absent`。
537
+ 4. POST 时带稳定 `source`、完整的 `expectedVersions` 与 `idempotencyKey`。
538
+ 5. 服务端在同一 Workflow 写锁内原子执行“校验所有 key → 合并 → 落盘”;无关 key 的变化不会冲突。
539
+ 6. 收到 `409` 后只刷新 `conflict.conflicts` 列出的 key,重新计算并重试一次。
200
540
 
201
- 1. Read the Workflow.
202
- 2. Retain `snapshot.runtimeRevision`.
203
- 3. Compute the semantic patch and complete derived projection.
204
- 4. Report with `expectedRevision`.
205
- 5. On 409, read again, reapply the same semantic intent, and retry once.
541
+ 不得在资源 key 冲突后原样重放旧 payload。
206
542
 
207
- Do not blindly replace remote state after a conflict.
543
+ ### 9.2 幂等键
208
544
 
209
- Use an idempotency key that identifies the logical operation, not the HTTP attempt:
545
+ 幂等键标识业务操作,不标识 HTTP 尝试:
210
546
 
211
547
  ```text
212
548
  <operation>:<scope>:<entity>:<semantic-version>
213
549
  ```
214
550
 
215
- Examples:
551
+ 例如:
216
552
 
217
553
  ```text
218
- implementation-finished:android:issue-2:v1
219
- timeline-membership:tapd-1015046:android-5.63.0:v1
554
+ implementation-finished:android:runtime-hook:v1
555
+ timeline-membership:tapd-1020124:android-version-1133202860001000338:v1
220
556
  ```
221
557
 
222
- A replay may return `alreadyApplied: true`; treat it as successful completion.
558
+ 同一 `workflow + source + operation + idempotencyKey` 的重放返回 `alreadyApplied: true`,包括 `running/error/pending` Action。`report` `artifact.publish` 可以安全复用同一业务 key。Publish 会返回第一次创建的预览,不会先生成一个新文件再去重。业务内容发生变化时提高语义版本或使用内容摘要;不要使用请求时间。
223
559
 
224
- ## 10. Producer integration procedure
560
+ ### 9.3 错误码
225
561
 
226
- Implement the producer adapter in this order:
562
+ | HTTP | 含义 | 处理方式 |
563
+ | --- | --- | --- |
564
+ | `400` | JSON、namespace、字段或 schema 不合法 | 按协议修正;不要降级校验 |
565
+ | `401` | 缺少或无效认证 | 停止并配置 Token;不要把 Token 打印出来 |
566
+ | `403` | 当前用户只有 viewer 权限或无权访问目标项目 | 停止;由 Owner 授予 Reporter 或改用正确身份 |
567
+ | `404` | 分享链接、owner 或目标资源不存在 | 重新解析目标,不要创建影子副本 |
568
+ | `409` | 同一资源 key 已变化,或路径属于其他 source | 读取 `conflict.conflicts`,只刷新冲突资源并重试一次;所有 key 通过前请求不会部分落库 |
569
+ | `500` | 服务端异常 | 保留幂等键,记录脱敏上下文后重试或上报 |
227
570
 
228
- 1. Define its private `globalState` schema outside AgentFlow.
229
- 2. Define a deterministic function from current producer state to the complete `projections.timeline` array.
230
- 3. Make projection identities stable across title and date changes.
231
- 4. Read the current materialized Workflow before reporting.
232
- 5. Patch only owned global-state fields.
233
- 6. Report the complete derived projection with the same operation when relevant.
234
- 7. Persist or derive a stable idempotency key.
235
- 8. Handle one revision-conflict retry.
236
- 9. Verify the returned materialized state and projection.
237
- 10. Confirm the personal and team iteration views group the Workflow correctly.
571
+ ## 10. 三个关键接入场景
238
572
 
239
- ## 11. Examples
573
+ ### 10.1 更新迭代:绑定或切换版本
240
574
 
241
- ### Action, artifact, state, and timeline together
575
+ 1. 从业务系统取得稳定版本 ID、标题、日期和平台。
576
+ 2. GET 当前 Workflow,并读取对应 GlobalState 路径与 Projection key 的资源版本。
577
+ 3. 用 `globalState.patch` 保存生产方拥有的完整版本事实。
578
+ 4. 发送当前 source 的完整 `kind=version` 切片;服务端保留其他 source 的 Sprint/Version。
579
+ 5. 使用这些 key 的 `expectedVersions` 上报。
242
580
 
243
- ```json
244
- {
245
- "schemaVersion": 1,
246
- "workflow": { "namespace": "tapd", "id": "1015046" },
247
- "source": "prd-flow",
248
- "action": {
249
- "key": "implementation:android:issue-2",
250
- "title": "Android 实现完成",
251
- "status": "done",
252
- "group": "implementation",
253
- "platform": "android",
254
- "issueKey": "issue-2"
255
- },
256
- "artifacts": [
257
- {
258
- "key": "implementation-mr:issue-2:android",
259
- "type": "gitlab-mr",
260
- "title": "Android 实现 MR",
261
- "url": "https://git.example.test/group/project/-/merge_requests/123",
262
- "scope": "action",
263
- "status": "ready"
264
- }
265
- ],
266
- "globalState": {
267
- "mode": "merge",
268
- "patch": {
269
- "prdFlowOwnedState": {
270
- "status": "implementing"
271
- }
272
- },
273
- "remove": []
274
- },
275
- "projections": {
276
- "timeline": [
277
- {
278
- "kind": "version",
279
- "id": "android-5.63.0",
280
- "title": "Likee Android 5.63.0",
281
- "date": "2026-08-20",
282
- "source": "prd-flow",
283
- "dimensions": { "platform": "android" }
284
- }
285
- ]
286
- },
287
- "expectedRevision": "runtime:replace-with-current-revision",
288
- "idempotencyKey": "implementation-finished:android:issue-2:v1"
289
- }
290
- ```
581
+ 版本改名或改期时保持 `id/key` 不变,只改 `title/date`;切换版本时移除旧自有 key、加入新 key;取消归属时只移除自己的版本条目。
291
582
 
292
- ### Projection-only synchronization
583
+ ### 10.2 上报 Action 与产物链接
293
584
 
294
- ```json
295
- {
296
- "workflow": { "namespace": "tapd", "id": "1015046" },
297
- "source": "prd-flow",
298
- "projections": {
299
- "timeline": [
300
- {
301
- "kind": "sprint",
302
- "id": "2026-w32",
303
- "title": "2026 第 32 周",
304
- "date": "2026-08-03",
305
- "source": "prd-flow",
306
- "dimensions": { "team": "client" }
307
- }
308
- ]
309
- },
310
- "expectedRevision": "runtime:replace-with-current-revision",
311
- "idempotencyKey": "timeline-membership:tapd-1015046:2026-w32:v1"
312
- }
313
- ```
585
+ 1. 先完成真实业务动作,例如创建 MR 或完成构建。
586
+ 2. 使用稳定 `action.key` 上报阶段结果。
587
+ 3. 已有 HTTP URL MR、构建、测试报告直接放入同一 Report 的 `artifacts`。
588
+ 4. 本地 Markdown 先调用 Artifact Publish,取得 URL 后再作为阶段证据使用。
589
+ 5. 重复刷新同一阶段继续使用原 key;不要每次新建 Action。
314
590
 
315
- ### Clear timeline membership
591
+ ### 10.3 上报自定义区域
316
592
 
317
- ```json
318
- {
319
- "workflow": { "namespace": "tapd", "id": "1015046" },
320
- "projections": { "timeline": [] },
321
- "expectedRevision": "runtime:replace-with-current-revision",
322
- "idempotencyKey": "timeline-membership-clear:tapd-1015046:v1"
323
- }
324
- ```
593
+ 1. 先判断 `globalState.sections` 的 `text/user/chips/list/link` 是否足够;足够时直接使用通用渲染器。
594
+ 2. 只有通用组件不能表达时,才定义稳定 namespace 和版本化扩展 schema。
595
+ 3. 把专用结构化事实放入 `extensions[namespace]`。
596
+ 4. AgentFlow 前端代码中注册对应页面渲染器并重新发布;当前不是运行时插件注册。否则数据只会被保存,不会自动出现专用 UI。当前只有 `extensions["prd-flow"]` 已注册。
597
+ 5. 更新数组时发送该数组的完整新值;更新对象字段时可以递归 merge;用 `null` 删除自有字段。
598
+
599
+ ## 11. prd-flow 参考映射
325
600
 
326
- ## 12. Acceptance checklist
327
-
328
- - The producer can read the current Workflow using only token-backed configuration.
329
- - The producer never prints or stores the token in report data.
330
- - Global-state changes preserve unrelated fields.
331
- - Actions and artifacts use stable keys.
332
- - Timeline entries use stable `kind` and `id` values.
333
- - Timeline replacement and explicit clearing both work.
334
- - Revision conflicts trigger one read-merge-retry cycle.
335
- - Replaying an idempotency key does not duplicate visible state.
336
- - Returned `snapshot.runtimeRevision` changes after a real update.
337
- - Personal and team iteration pages show the same canonical grouping for accessible Workflows.
601
+ prd-flow 只是一个接入实现,不是协议依赖:
602
+
603
+ | prd-flow 事实 | 通用协议位置 | 页面结果 |
604
+ | --- | --- | --- |
605
+ | TAPD short ID | `workflow = tapd:<id>` | 串起同一需求、权限和分享 |
606
+ | TAPD Owner / 参与人 | `POST /api/workflows/access/sync` | Owner 管理权限;参与人默认只读 |
607
+ | 计算出的完整当前状态 | `observation.state` | Workflow 全局概览和当前指针 |
608
+ | TAPD 当前版本原始信息 | `globalState.tapdCurrentVersion` | 保留版本业务事实 |
609
+ | 由版本事实派生的归属 | `projections.timeline[kind=version]` | 个人/团队迭代时间线 |
610
+ | 方案确认、实现、提测、发布 | `action` | Action 时间轴和进度 |
611
+ | MR、Jenkins、测试报告 URL | `artifacts` | Action 下产物入口 |
612
+ | 本地方案 Markdown | Artifact Publish | 可分享方案预览 URL |
613
+ | AI Docs / Issues | `extensions["prd-flow"]` | prd-flow 专用文档区和 Issue 区 |
614
+
615
+ 旧的 `/api/prd-workflow/snapshot`、`/api/prd-workflow/event`、`/api/prd-workflow/review-link` 仅供旧客户端迁移,返回弃用提示。新接入不得使用。
616
+
617
+ ## 12. 验收清单
618
+
619
+ - 能使用 Token GET 当前 Workflow,并读到 `snapshot.resourceVersions`。
620
+ - TAPD Owner 同步后成为 Workflow Owner;TAPD 参与人自动成为 Viewer;显式 Reporter 能写,Viewer、团队成员、分享链接和管理员代看不能写。
621
+ - TAPD 派生参与人刷新不会覆盖显式授权;过期的权限快照返回 `409`。
622
+ - `globalState` 更新不会覆盖其他生产方拥有的路径,数组替换行为符合预期。
623
+ - 同一 Action key 重报不产生重复业务阶段。
624
+ - Action 下能看到稳定 key 的 MR、构建或测试产物。
625
+ - Markdown Publish 返回可访问 URL,但不会推进业务状态。
626
+ - 版本改名/改期不产生新迭代节点,版本切换不会删除第三方 Sprint。
627
+ - 自定义 extension 能保存;注册渲染器后能显示对应文档区 / Issue 区。
628
+ - 不同资源 key 可并发更新;同 key 旧版本返回包含具体 `resourceKey` 的 409。
629
+ - 409 会触发一次 key 级 read → re-merge → retry,且失败请求不会部分落库。
630
+ - 幂等重放返回成功且不重复应用。
631
+ - Token 不出现在 JSON、日志、Artifact 或最终输出中。