weflow-cli 1.6.4 → 1.7.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/CHANGELOG.md +18 -0
- package/OPERATIONS.md +35 -0
- package/bin/weflow-cli.ts +312 -0
- package/dist/bin/weflow-cli.js +352 -0
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/src/core/ntCore.d.ts +26 -0
- package/dist/src/core/ntCore.d.ts.map +1 -1
- package/dist/src/core/ntCore.js +21 -0
- package/dist/src/core/ntCore.js.map +1 -1
- package/dist/src/services/chatService.d.ts +13 -0
- package/dist/src/services/chatService.d.ts.map +1 -1
- package/dist/src/services/chatService.js +20 -0
- package/dist/src/services/chatService.js.map +1 -1
- package/dist/src/services/messageQuery.d.ts +35 -0
- package/dist/src/services/messageQuery.d.ts.map +1 -1
- package/dist/src/services/messageQuery.js +61 -0
- package/dist/src/services/messageQuery.js.map +1 -1
- package/dist/src/services/syncService.d.ts +121 -0
- package/dist/src/services/syncService.d.ts.map +1 -0
- package/dist/src/services/syncService.js +244 -0
- package/dist/src/services/syncService.js.map +1 -0
- package/dist/src/services/syncState.d.ts +113 -0
- package/dist/src/services/syncState.d.ts.map +1 -0
- package/dist/src/services/syncState.js +214 -0
- package/dist/src/services/syncState.js.map +1 -0
- package/docs/DECISIONS.md +60 -20
- package/docs/FUNCTIONAL-IMPROVEMENT-SPEC.md +338 -0
- package/docs/PROJECT_STATE.md +5 -3
- package/docs/SYNC_CONTRACT.md +152 -0
- package/package.json +1 -1
- package/scripts/export_chat_html.py +177 -13
- package/scripts/nt_decrypt.py +123 -15
- package/src/core/ntCore.ts +42 -0
- package/src/services/chatService.ts +26 -0
- package/src/services/messageQuery.ts +97 -0
- package/src/services/syncService.ts +335 -0
- package/src/services/syncState.ts +275 -0
package/docs/DECISIONS.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> Record decisions that affect long-term maintenance. Each entry explains the chosen direction and the reason, not every implementation detail.
|
|
4
4
|
|
|
5
|
+
> 编号说明:`D-009` 从未使用(D-008 直接跳到 D-010),保留为空号;
|
|
6
|
+
> 历史上曾有两条决策共用 `D-016`,其中「architecture visuals」一条改号为 `D-028`。
|
|
7
|
+
|
|
5
8
|
## D-001: Local-first data handling
|
|
6
9
|
|
|
7
10
|
**Status:** Active
|
|
@@ -114,6 +117,16 @@ HTML export selects message-resource records by nonzero server message ID. It do
|
|
|
114
117
|
|
|
115
118
|
**Consequences:** WeChat 4.x V2 media keys are derived from local `kvcomm` data, verified against a real cached V2 header, and used only in memory during export. Remote emoticons are decrypted with message-provided keys; entity-escaped XML is normalized; and URL fallbacks cover encrypted, thumbnail, CDN, and external fields. Structured app cards keep their links and use cached or resolved covers when available. Some messages remain placeholders when reliable identity or source media is unavailable. Synthetic tests cover these paths; reporter verification is still needed for issue #7.
|
|
116
119
|
|
|
120
|
+
## D-015: Keep documentation synchronized with the source baseline
|
|
121
|
+
|
|
122
|
+
**Status:** Active
|
|
123
|
+
|
|
124
|
+
User-facing setup and troubleshooting documents describe supported commands and guarantees only when they are present in the current CLI and scripts. The project state and decision log remain the handoff source for agents; architecture explains boundaries and data flow; the README stays a short entry point.
|
|
125
|
+
|
|
126
|
+
**Reason:** The project has both a GitHub source workflow and a separately published npm package. Stale examples, hard-coded tool counts, or old compatibility claims can cause users to run the wrong code or expose sensitive data while troubleshooting.
|
|
127
|
+
|
|
128
|
+
**Consequences:** When command options, platform support, data flow, security boundaries, or verification status changes, update the relevant document in the same change. Validate examples against `--help` and keep generated local output out of commits.
|
|
129
|
+
|
|
117
130
|
## D-016: Establish a stable downstream data boundary
|
|
118
131
|
|
|
119
132
|
**Status:** Active
|
|
@@ -144,26 +157,6 @@ The CLI exposes `capabilities --json` as the first call for automation. Read-ori
|
|
|
144
157
|
|
|
145
158
|
**Consequences:** New user-facing commands should be added to the capability response and should declare whether they read local data, invoke AI, or cause side effects. This is an additive interface and does not change existing human-readable output.
|
|
146
159
|
|
|
147
|
-
## D-015: Keep documentation synchronized with the source baseline
|
|
148
|
-
|
|
149
|
-
**Status:** Active
|
|
150
|
-
|
|
151
|
-
User-facing setup and troubleshooting documents describe supported commands and guarantees only when they are present in the current CLI and scripts. The project state and decision log remain the handoff source for agents; architecture explains boundaries and data flow; the README stays a short entry point.
|
|
152
|
-
|
|
153
|
-
**Reason:** The project has both a GitHub source workflow and a separately published npm package. Stale examples, hard-coded tool counts, or old compatibility claims can cause users to run the wrong code or expose sensitive data while troubleshooting.
|
|
154
|
-
|
|
155
|
-
**Consequences:** When command options, platform support, data flow, security boundaries, or verification status changes, update the relevant document in the same change. Validate examples against `--help` and keep generated local output out of commits.
|
|
156
|
-
|
|
157
|
-
## D-016: Use GPT-image-2 for future architecture visuals
|
|
158
|
-
|
|
159
|
-
**Status:** Active
|
|
160
|
-
|
|
161
|
-
When a new architecture diagram visual is requested, use GPT-image-2 for the visual asset. Keep the diagram's structure and labels aligned with the source documentation, validate the final dimensions and legibility, and retain a maintainable source representation when practical.
|
|
162
|
-
|
|
163
|
-
**Reason:** The project owner wants architecture visuals to use the project's image-generation workflow while keeping technical documentation understandable and reviewable.
|
|
164
|
-
|
|
165
|
-
**Consequences:** Do not silently substitute an unrelated image-generation model. Do not treat generated pixels as the source of truth; `ARCHITECTURE.md` and the code remain authoritative.
|
|
166
|
-
|
|
167
160
|
## D-019: Require preview and confirmation for side effects
|
|
168
161
|
|
|
169
162
|
**Status:** Active
|
|
@@ -254,6 +247,53 @@ The bundled WCDB DLL exposes a raw SQL query ABI without parameter binding. `exe
|
|
|
254
247
|
|
|
255
248
|
**Consequences:** Consumers can perform overlapping time-window reads and deduplicate locally. A stable incremental cursor remains a separate future change and must be supported by all relevant database backends before it is advertised.
|
|
256
249
|
|
|
250
|
+
## D-028: Use GPT-image-2 for future architecture visuals
|
|
251
|
+
|
|
252
|
+
**Status:** Active
|
|
253
|
+
|
|
254
|
+
When a new architecture diagram visual is requested, use GPT-image-2 for the visual asset. Keep the diagram's structure and labels aligned with the source documentation, validate the final dimensions and legibility, and retain a maintainable source representation when practical.
|
|
255
|
+
|
|
256
|
+
**Reason:** The project owner wants architecture visuals to use the project's image-generation workflow while keeping technical documentation understandable and reviewable.
|
|
257
|
+
|
|
258
|
+
**Consequences:** Do not silently substitute an unrelated image-generation model. Do not treat generated pixels as the source of truth; `ARCHITECTURE.md` and the code remain authoritative.
|
|
259
|
+
|
|
260
|
+
## D-029: Keep the sync and media-coverage work additive
|
|
261
|
+
|
|
262
|
+
**Status:** Active
|
|
263
|
+
|
|
264
|
+
`weflow-sync/v1` and `weflow-job/v1` are new local state contracts written under
|
|
265
|
+
`~/.weflow-cli/`. The `sync` command and the export media report are additive: no existing command
|
|
266
|
+
changes its behaviour, its stdout contract, or its JSON schema. `export <talker> html` gains one
|
|
267
|
+
side file (`<prefix>_media.json`) and one extra key in its trailing stdout JSON, both of which the
|
|
268
|
+
only consumer ignores; `--media-report 0` restores the previous file set exactly.
|
|
269
|
+
|
|
270
|
+
**Reason:** D-016 fences the exported message contract and D-027 fences coverage metadata, so a
|
|
271
|
+
change that redefines existing fields would need a compatibility review this work does not warrant.
|
|
272
|
+
The gaps being closed - shard read failures dropped silently, media counters computed and discarded
|
|
273
|
+
- are observations about what already happened, not redefinitions.
|
|
274
|
+
|
|
275
|
+
**Consequences:**
|
|
276
|
+
|
|
277
|
+
- Sync state is **one file per conversation** (`sync/<source>-<name>-<hash>.json`), so every write is
|
|
278
|
+
a whole-file atomic replace and no cross-conversation read-modify-write merge is needed. The hash
|
|
279
|
+
suffix keeps `wxid_a@chatroom` and `wxid_a_chatroom` distinct without putting display names in a
|
|
280
|
+
directory listing.
|
|
281
|
+
- `lastAttempt` and `lastSuccessfulRun` advance separately. A run that is `partial` still writes its
|
|
282
|
+
state and its job record, but must not advance `lastSuccessfulRun` and must not return success
|
|
283
|
+
(the spec's rule that partial completion is not success).
|
|
284
|
+
- The message identity keeps `serverId` when nonzero and otherwise uses `localId` + `createTime`.
|
|
285
|
+
It **omits `shard`**, deviating from the spec's §4.2 sketch: messages do not carry a shard field,
|
|
286
|
+
adding one would touch the fenced export contract, and overlap deduplication does not need it.
|
|
287
|
+
Add it only if a cross-shard collision is actually observed.
|
|
288
|
+
- `sync retry` is **not** implemented. With no local index and no partial writes it would be exactly
|
|
289
|
+
the same operation as re-running the window, and the failed-shard list is already in `sync status`.
|
|
290
|
+
A command that is a synonym is worse than no command.
|
|
291
|
+
- `sync` is the only writer of `weflow-job/v1` in this round. If a single consumer is judged not to
|
|
292
|
+
justify the file, the reversal is: delete the job store and fold `lastJob` into each
|
|
293
|
+
`weflow-sync/v1` file. Nothing else reads it, so that is a one-file change.
|
|
294
|
+
- Still not advertised: a stable incremental cursor. D-027 stands - overlapping windows plus local
|
|
295
|
+
deduplication is what this exposes.
|
|
296
|
+
|
|
257
297
|
## Decision Template
|
|
258
298
|
|
|
259
299
|
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# WeFlow CLI 功能增强技术方案
|
|
2
|
+
|
|
3
|
+
> 状态:提案
|
|
4
|
+
>
|
|
5
|
+
> 更新时间:2026-09-19
|
|
6
|
+
>
|
|
7
|
+
> 适用范围:CLI、MCP、本地阅读器、聊天导出、日报、知识库和可选助手
|
|
8
|
+
|
|
9
|
+
本文把 WeFlow CLI 下一阶段的功能改进整理为可拆分的技术方案。它补充 [ROADMAP.md](ROADMAP.md) 的方向说明,面向实现、评审和验收;已经实现的能力以 [PROJECT_STATE.md](PROJECT_STATE.md) 为准。
|
|
10
|
+
|
|
11
|
+
## 1. 产品目标
|
|
12
|
+
|
|
13
|
+
WeFlow CLI 已经覆盖本地聊天读取、联系人与收藏、HTML/JSON/TXT/Excel 导出、公众号日报、知识库检索、RAG、只读 MCP 和 Bot 助手。下一阶段不以增加零散命令为主要目标,而是把这些能力连接成四条可恢复、可验证的工作流:
|
|
14
|
+
|
|
15
|
+
1. **同步:** 从本地数据源读取增量变化,并明确哪些数据已覆盖、哪些数据读取失败。
|
|
16
|
+
2. **整理:** 将消息、媒体、文章和笔记写入统一的本地索引,保留原始来源。
|
|
17
|
+
3. **使用:** 通过搜索、阅读器、导出和 MCP 查询这些数据,并能回到原始记录。
|
|
18
|
+
4. **分析:** 在用户选择的范围内调用本地或云端 AI,输出带来源的结果。
|
|
19
|
+
|
|
20
|
+
功能完成的标准不是命令数量,而是用户可以在失败后继续运行、在结果中看到覆盖边界,并能验证 AI 或导出结果来自哪些原始资料。
|
|
21
|
+
|
|
22
|
+
## 2. 现状与功能缺口
|
|
23
|
+
|
|
24
|
+
| 能力 | 当前基础 | 需要补齐的功能 |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| 本地数据 | 支持会话、联系人、消息、收藏、朋友圈缓存和多种导出;已处理部分消息分片 | 增量同步、稳定游标、覆盖报告、失败分片重试和统一索引 |
|
|
27
|
+
| 媒体导出 | 支持图片、表情、视频、链接卡片,部分场景支持语音转写 | 媒体清单、缺失原因、去重、导出断点和可迁移资料包 |
|
|
28
|
+
| 日报阅读 | 支持来源筛选、分类、摘要、HTML 阅读器和收藏同步 | 来源健康、抓取重试、文章去重、阅读状态和离线归档 |
|
|
29
|
+
| 搜索与知识库 | 支持语义搜索、RAG、Vault 和阅读笔记 | 混合检索、增量索引、引用定位、结果解释和评估集 |
|
|
30
|
+
| MCP | 默认只读、有限制、使用版本化消息契约 | 稳定游标、更多只读资源、统一错误码和任务状态查询 |
|
|
31
|
+
| 助手 | 有隐私模式、白名单、Bot 通道和确认边界 | 收件箱/发件箱、幂等发送、投递状态和会话级记忆控制 |
|
|
32
|
+
|
|
33
|
+
## 3. 优先级和交付顺序
|
|
34
|
+
|
|
35
|
+
### P0:数据可信和任务可恢复
|
|
36
|
+
|
|
37
|
+
先完成增量同步、统一任务状态和导出覆盖报告。这一阶段解决“有没有读全”“导出到哪一步”“缺的媒体是什么”三个问题。
|
|
38
|
+
|
|
39
|
+
### P1:检索和阅读闭环
|
|
40
|
+
|
|
41
|
+
在可信数据基础上完成混合搜索、引用定位、日报增量抓取和离线阅读。此阶段应让用户能够从一篇 AI 摘要或一个搜索结果返回到原始消息或文章。
|
|
42
|
+
|
|
43
|
+
### P2:受控 AI 和只读生态
|
|
44
|
+
|
|
45
|
+
统一 AI 执行层、MCP 增量接口和助手收发队列。所有云端分析和外发操作继续沿用预览、确认和隐私模式。
|
|
46
|
+
|
|
47
|
+
### P3:迁移和平台扩展
|
|
48
|
+
|
|
49
|
+
最后增加资料包迁移、Markdown/Obsidian 等互操作格式,以及 Linux/macOS 的能力适配。平台扩展必须建立在能力检测和明确降级结果上。
|
|
50
|
+
|
|
51
|
+
## 4. 统一数据同步和索引
|
|
52
|
+
|
|
53
|
+
### 4.1 模块划分
|
|
54
|
+
|
|
55
|
+
建议把同步流程拆成以下模块:
|
|
56
|
+
|
|
57
|
+
| 模块 | 责任 |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `SourceAdapter` | 从 NT 数据库、传统数据库、日报目录或文章源读取记录 |
|
|
60
|
+
| `Normalizer` | 将不同来源转换为统一记录,保留未知字段 |
|
|
61
|
+
| `IdentityResolver` | 生成消息、文章和媒体的稳定身份,拒绝不可靠匹配 |
|
|
62
|
+
| `CheckpointStore` | 保存分片、时间窗、游标和失败重试状态 |
|
|
63
|
+
| `LocalIndex` | 写入全文索引、向量索引和元数据索引 |
|
|
64
|
+
| `CoverageReporter` | 统计读取范围、缺失分片、媒体覆盖和重复记录 |
|
|
65
|
+
|
|
66
|
+
### 4.2 消息身份
|
|
67
|
+
|
|
68
|
+
在稳定游标尚未得到所有后端支持前,使用组合身份并允许重叠读取:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
messageKey = source + talker + shard + localId + serverId + createTime
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
其中 `serverId` 为非零值时优先使用;`localId` 不能单独作为全局身份。时间窗同步应保留少量重叠范围,由本地去重保证边界消息不丢失。
|
|
75
|
+
|
|
76
|
+
### 4.3 同步状态示例
|
|
77
|
+
|
|
78
|
+
状态文件不得包含密钥、完整聊天正文或无必要的本地绝对路径。可以保存如下信息:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"schema": "weflow-sync/v1",
|
|
83
|
+
"source": "wechat-nt",
|
|
84
|
+
"scope": "示例联系人",
|
|
85
|
+
"lastSuccessfulRun": "2026-09-19T09:00:00+08:00",
|
|
86
|
+
"coveredFrom": "2026-09-01T00:00:00+08:00",
|
|
87
|
+
"coveredTo": "2026-09-19T08:59:59+08:00",
|
|
88
|
+
"recordsRead": 1200,
|
|
89
|
+
"recordsDeduplicated": 14,
|
|
90
|
+
"shardsRead": 4,
|
|
91
|
+
"shardsFailed": 0,
|
|
92
|
+
"mayHaveMore": false,
|
|
93
|
+
"warnings": []
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4.4 建议入口
|
|
98
|
+
|
|
99
|
+
具体命令名称可以由实现者决定,但应覆盖以下能力:
|
|
100
|
+
|
|
101
|
+
- `sync --dry-run --json`:只返回范围、预计记录数和风险标记;
|
|
102
|
+
- `sync run`:执行全量或增量同步;
|
|
103
|
+
- `sync status --json`:查看最后一次运行、覆盖范围和失败项;
|
|
104
|
+
- `sync retry`:只重跑失败分片或失败来源;
|
|
105
|
+
- `sync verify`:检查索引与原始数据的时间边界和计数摘要。
|
|
106
|
+
|
|
107
|
+
## 5. 统一任务中心
|
|
108
|
+
|
|
109
|
+
导出、日报、索引、语音转写和知识处理应共享任务模型,而不是各自实现一套进度逻辑。
|
|
110
|
+
|
|
111
|
+
### 5.1 生命周期
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
planned → waiting_confirmation → running → paused
|
|
115
|
+
↘ completed
|
|
116
|
+
↘ failed
|
|
117
|
+
↘ cancelled
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
任务必须具备稳定的 `jobId` 和幂等键。重复提交同一范围的任务时,应返回已有任务或明确创建新任务,不能悄悄重复写入。
|
|
121
|
+
|
|
122
|
+
### 5.2 任务记录
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"schema": "weflow-job/v1",
|
|
127
|
+
"jobId": "job_20260919_0001",
|
|
128
|
+
"kind": "export.html",
|
|
129
|
+
"state": "running",
|
|
130
|
+
"createdAt": "2026-09-19T09:00:00+08:00",
|
|
131
|
+
"updatedAt": "2026-09-19T09:02:10+08:00",
|
|
132
|
+
"progress": { "completed": 800, "total": 1200 },
|
|
133
|
+
"resumeable": true,
|
|
134
|
+
"result": { "records": 800, "mediaMissing": 3 },
|
|
135
|
+
"error": null
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
任务输出应区分“已完成”“部分完成”和“失败”。部分完成不能返回成功状态,也不能删除已生成的可用结果。
|
|
140
|
+
|
|
141
|
+
### 5.3 用户体验
|
|
142
|
+
|
|
143
|
+
所有长任务至少提供:
|
|
144
|
+
|
|
145
|
+
- 预览和预计影响;
|
|
146
|
+
- 进度、当前阶段和耗时;
|
|
147
|
+
- 暂停、恢复、取消;
|
|
148
|
+
- 失败原因和可重试动作;
|
|
149
|
+
- 人类可读输出与机器可读 JSON 输出。
|
|
150
|
+
|
|
151
|
+
## 6. 搜索、引用和知识库
|
|
152
|
+
|
|
153
|
+
### 6.1 混合检索流程
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
用户查询
|
|
157
|
+
→ 查询解析(关键词、日期、联系人、会话、消息类型)
|
|
158
|
+
→ 全文检索 + 向量检索
|
|
159
|
+
→ 元数据过滤
|
|
160
|
+
→ 去重和重排
|
|
161
|
+
→ 片段裁剪
|
|
162
|
+
→ 引用生成
|
|
163
|
+
→ AI 回答或结构化结果
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
关键词检索负责精确命中姓名、编号和原句;向量检索负责语义相近内容。两者都必须受到日期、会话和结果数量限制。
|
|
167
|
+
|
|
168
|
+
### 6.2 引用对象
|
|
169
|
+
|
|
170
|
+
每条引用至少包含:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"sourceType": "wechat-message",
|
|
175
|
+
"sourceId": "messageKey",
|
|
176
|
+
"talker": "示例联系人",
|
|
177
|
+
"createdAt": "2026-09-19T08:30:00+08:00",
|
|
178
|
+
"snippet": "经脱敏或用户允许的原文片段",
|
|
179
|
+
"locator": { "export": "示例联系人", "part": 1 }
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
AI 输出中的事实性结论应能回到一个或多个引用;没有引用的内容应标记为推断或建议。原始消息和 AI 生成文本必须分开保存。
|
|
184
|
+
|
|
185
|
+
### 6.3 索引更新
|
|
186
|
+
|
|
187
|
+
- 只对新增或变更记录建立索引;
|
|
188
|
+
- 内容哈希变化时重建对应条目;
|
|
189
|
+
- 删除或失效记录进入 tombstone,避免旧结果继续出现;
|
|
190
|
+
- 向量模型变化时支持新旧索引并行切换;
|
|
191
|
+
- 建立不含真实聊天内容的评估集,测试召回率、引用准确率和重复率。
|
|
192
|
+
|
|
193
|
+
## 7. 导出资料包和媒体覆盖
|
|
194
|
+
|
|
195
|
+
### 7.1 导出配置
|
|
196
|
+
|
|
197
|
+
导出应从单一命令扩展为可保存的配置:
|
|
198
|
+
|
|
199
|
+
- 会话或联系人范围;
|
|
200
|
+
- 日期范围;
|
|
201
|
+
- 输出格式;
|
|
202
|
+
- 是否包含图片、表情、视频、语音和远程封面;
|
|
203
|
+
- 是否脱敏;
|
|
204
|
+
- 每页条数和媒体大小上限;
|
|
205
|
+
- 离线模式或允许远程读取。
|
|
206
|
+
|
|
207
|
+
### 7.2 媒体清单
|
|
208
|
+
|
|
209
|
+
每个媒体项应记录状态,而不是只在页面中显示占位符:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"mediaKey": "content-or-message-identity",
|
|
214
|
+
"kind": "image",
|
|
215
|
+
"status": "embedded",
|
|
216
|
+
"mime": "image/jpeg",
|
|
217
|
+
"bytes": 123456,
|
|
218
|
+
"sha256": "redacted-example",
|
|
219
|
+
"reason": null
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
推荐状态:`embedded`、`cached`、`remote-fetched`、`missing`、`untrusted-match`、`unsupported`。无法可靠匹配时必须保留缺失原因,不能用相邻消息或裸 `localId` 猜测媒体。
|
|
224
|
+
|
|
225
|
+
### 7.3 离线资料包
|
|
226
|
+
|
|
227
|
+
资料包建议包含:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
manifest.json
|
|
231
|
+
messages.json
|
|
232
|
+
index.html
|
|
233
|
+
media/
|
|
234
|
+
attachments/
|
|
235
|
+
README.txt
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`manifest.json` 保存数据范围、生成时间、版本、覆盖统计和脱敏选项;不得保存数据库密钥、完整本地路径或未授权的配置内容。
|
|
239
|
+
|
|
240
|
+
## 8. 日报和本地阅读器
|
|
241
|
+
|
|
242
|
+
### 8.1 来源状态
|
|
243
|
+
|
|
244
|
+
每个公众号或文章来源应有本地状态:启用、停用、最近成功时间、最近失败原因、连续失败次数和最后抓取文章时间。状态用于提醒用户,不应自动扩大抓取范围。
|
|
245
|
+
|
|
246
|
+
### 8.2 文章去重
|
|
247
|
+
|
|
248
|
+
优先使用文章 URL,其次使用来源、标题、发布时间和正文哈希组合判断重复。重复文章应保留来源关系,但不重复生成日报和向量索引。
|
|
249
|
+
|
|
250
|
+
### 8.3 阅读状态
|
|
251
|
+
|
|
252
|
+
阅读器可以增加未读、已读、稍后阅读、收藏和笔记关联。所有状态保存在本地,刷新、重新生成 HTML 或重启服务后仍能恢复。
|
|
253
|
+
|
|
254
|
+
## 9. 受控 AI 执行层
|
|
255
|
+
|
|
256
|
+
统一 AI 配置应至少包含:
|
|
257
|
+
|
|
258
|
+
| 配置 | 作用 |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| provider/model | 选择云端或本地模型 |
|
|
261
|
+
| privacyMode | `strict`、`balanced`、`local` |
|
|
262
|
+
| inputLimit | 限制消息数量、字符数或 token 数 |
|
|
263
|
+
| timeLimit | 限制单任务耗时 |
|
|
264
|
+
| budget | 限制调用次数或费用 |
|
|
265
|
+
| outputSchema | 约束结构化输出 |
|
|
266
|
+
| citationRequired | 要求返回引用 |
|
|
267
|
+
|
|
268
|
+
AI 调用前应生成不含正文的预览,说明数据范围、是否联网、预计规模和写入位置。云端调用失败时,保留本地抓取和无 AI 结果,不把整个工作流判定为不可用。
|
|
269
|
+
|
|
270
|
+
## 10. MCP 和助手功能
|
|
271
|
+
|
|
272
|
+
### 10.1 MCP 只读扩展
|
|
273
|
+
|
|
274
|
+
优先扩展以下只读能力:
|
|
275
|
+
|
|
276
|
+
- 会话发现和唯一名称解析;
|
|
277
|
+
- 分页读取消息和覆盖状态;
|
|
278
|
+
- 联系人、收藏、日报和知识条目查询;
|
|
279
|
+
- 导出任务创建、查询和结果摘要;
|
|
280
|
+
- 能力、契约和限制发现。
|
|
281
|
+
|
|
282
|
+
分页建议使用不透明游标,响应带 `nextCursor`、`returnedCount`、`mayHaveMore` 和请求范围。错误响应使用稳定错误码,避免让客户端解析中文错误文本。
|
|
283
|
+
|
|
284
|
+
### 10.2 Bot 助手收发队列
|
|
285
|
+
|
|
286
|
+
助手只围绕已验证的 Bot 通道增强,不扩展为个人微信账号自动化。建议引入:
|
|
287
|
+
|
|
288
|
+
- 入站事件 ID 和去重表;
|
|
289
|
+
- 出站消息队列;
|
|
290
|
+
- `queued`、`sent`、`delivered`、`failed` 状态;
|
|
291
|
+
- 重试次数和最后失败原因;
|
|
292
|
+
- 会话级记忆开关和数据范围;
|
|
293
|
+
- 外发前的预览和确认。
|
|
294
|
+
|
|
295
|
+
## 11. 验收指标
|
|
296
|
+
|
|
297
|
+
每个阶段至少记录以下指标:
|
|
298
|
+
|
|
299
|
+
| 指标 | 目标 |
|
|
300
|
+
| --- | --- |
|
|
301
|
+
| 增量同步重复率 | 重复读取可被本地去重,不产生重复索引 |
|
|
302
|
+
| 分片覆盖率 | 可报告成功、失败和未扫描分片 |
|
|
303
|
+
| 媒体错配率 | 可靠匹配失败时宁缺毋滥,禁止跨会话错配 |
|
|
304
|
+
| 导出可恢复率 | 中断后从最近检查点继续,不必从头开始 |
|
|
305
|
+
| 搜索引用准确率 | 引用能定位到实际返回片段 |
|
|
306
|
+
| 日报重复率 | 同一文章重复运行不重复生成内容 |
|
|
307
|
+
| AI 数据边界 | 每次云端调用均可说明输入范围和隐私模式 |
|
|
308
|
+
| MCP 响应边界 | 结果数量、时间范围和字段均受限制 |
|
|
309
|
+
|
|
310
|
+
## 12. 风险和明确不做的事
|
|
311
|
+
|
|
312
|
+
- 不使用裸 `localId`、相邻记录或模糊名称猜测消息和媒体身份。
|
|
313
|
+
- 不把数据库路径、密钥、完整聊天正文或任意本地目录暴露给 MCP 客户端或合作应用。
|
|
314
|
+
- 不在默认工作流中加入个人微信账号自动发消息、拉群、群管理或未验证的客户端自动化。
|
|
315
|
+
- 不把云端 AI 作为日报、导出和本地搜索的必需依赖。
|
|
316
|
+
- 不在没有迁移方案、兼容性测试和回滚方式时修改现有 JSON 数据契约。
|
|
317
|
+
|
|
318
|
+
## 13. 建议的首轮开发任务
|
|
319
|
+
|
|
320
|
+
1. 定义 `weflow-sync/v1` 和 `weflow-job/v1` 两个内部状态契约。
|
|
321
|
+
2. 为消息同步增加检查点、重叠时间窗和覆盖摘要。
|
|
322
|
+
3. 为 HTML/JSON 导出增加媒体覆盖报告和部分完成状态。
|
|
323
|
+
4. 将导出、日报和索引任务接入统一任务状态查询。
|
|
324
|
+
5. 为搜索结果增加消息/文章引用对象,不改变现有原始数据字段。
|
|
325
|
+
6. 为 MCP 增加不透明游标和稳定错误码。
|
|
326
|
+
7. 使用合成数据建立同步、搜索、导出和 MCP 的端到端验收样例。
|
|
327
|
+
|
|
328
|
+
首轮完成后,再根据实际失败记录决定是否进入阅读状态、AI 执行层和助手收发队列,避免在数据边界尚未稳定时继续扩大功能面。
|
|
329
|
+
|
|
330
|
+
## 14. 关联文档
|
|
331
|
+
|
|
332
|
+
- [整体路线图](ROADMAP.md)
|
|
333
|
+
- [项目当前状态](PROJECT_STATE.md)
|
|
334
|
+
- [消息数据契约](DATA_CONTRACT.md)
|
|
335
|
+
- [MCP 接口说明](MCP.md)
|
|
336
|
+
- [AI 接口和确认协议](AI_INTERFACE.md)
|
|
337
|
+
- [技术决策记录](DECISIONS.md)
|
|
338
|
+
- [安全说明](../SECURITY.md)
|
package/docs/PROJECT_STATE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Project State
|
|
2
2
|
|
|
3
|
-
> Last reviewed: 2026-09-19. This is the current maintenance snapshot, not a release note. Keep it factual and update it with meaningful project changes.
|
|
3
|
+
> Last reviewed: 2026-09-19 (sync and media coverage added). This is the current maintenance snapshot, not a release note. Keep it factual and update it with meaningful project changes.
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
@@ -25,14 +25,16 @@ WeFlow CLI is a local-first command-line tool and MCP server for user-authorized
|
|
|
25
25
|
| Initialization | Verify and reuse existing local database access by default; refresh only when needed. Missing-key tests can run without changing saved configuration. | `init`, `init --refresh`, `init --test-missing-keys`, `config forget-keys`, `check` |
|
|
26
26
|
| Official-account daily | Filter configured sources, preserve source categories, backfill incomplete yesterday output before an unqualified today run, fetch articles, create summaries, generate a local HTML reader, and synchronize reader favorites into local files. | `daily`, `daily favorites`, `daily-stats`, `daily-server` |
|
|
27
27
|
| Knowledge workflows | Wiki compilation, semantic search, RAG, WeRead sync, reviews, reading notes, and staged Vault promotion. | `wiki`, `vault`, `search`, `chat`, `weread`, `review` |
|
|
28
|
+
| Incremental read | `sync run` reads a time window, deduplicates against the previous checkpoint and writes coverage state; `sync status` reports it without touching the database; `sync verify` re-reads the recorded window and compares bounds and counts. Every run reports `coverage` as `complete`, `unverified` or `partial`, and a partial run never advances `lastSuccessfulRun`. It does **not** offer a stable cursor - see D-027. | `sync run`, `sync status`, `sync verify` |
|
|
29
|
+
| Media coverage | HTML export writes `<prefix>_media.json` with a status and reason per media item, and reports the remote-fetch budget it used. A media miss is recorded rather than shown as a bare placeholder. | `export <talker> html` |
|
|
28
30
|
| MCP | Local stdio MCP server exposes bounded read and local transformation tools, including the `wechat.export_messages` versioned message contract. Publishing, messaging, memory writes, todo mutation, configuration changes, and deletion are excluded. | `mcp-config`, `mcp-server/index.ts` |
|
|
29
31
|
| Assistant | Optional WeChat Bot-channel assistant with local memory and privacy gates. | `login-wechat`, `assistant` |
|
|
30
32
|
|
|
31
|
-
The 67-test TypeScript regression suite covers home-path expansion, custom NT data-root discovery, bounded daily-favorites synchronization, confirmed no-AI daily generation, no-AI Vault promotion, Vault content mutation previews, Vault initialization and synchronization confirmation, MCP configuration write confirmation, source/compiled Python resource lookup, outbound PII redaction, local-inference bypass, strict message-body masking, MCP path/date/URL validation and live tool discovery, message-contract preservation, local-date export bounds, export result metadata, evidence-package safety, assistant routing and lifecycle confirmation, message-channel authentication confirmation, todo and access-control mutation confirmation, message-send preview/confirmation, local-reader port validation, redacted configuration status, access-list JSON, database-key reset confirmation, human-gated initialization and key capture, content-free account-scan and assistant-log diagnostics, confirmed Vault RAG, confirmed semantic search and RAG chat, structured todo reminders, strict knowledge limits, report previews, and cover-image signature validation. Core automation entry points now include `capabilities --json`, `config show --json`, `whitelist list --json`, `blacklist list --json`, `check --json`, `init --dry-run --json`, `scan --json`, `dbkey --dry-run --json`, `sessions --json`, `messages --json`, `contacts --json`, `export ... --json`, `fav export ... --json-result`, `sns ... --json`, `daily-stats --json`, `daily --no-ai --dry-run --json`, `daily-server --status --json`, `assistant status --json`, `assistant log --json`, `todos list --json`, `todos remind --json`, `evidence --json`, `evidence-review --dry-run --json`, Vault mutation previews, confirmed `vault rag`, confirmed `search` and `chat`, daily-favorite previews, `search-index --dry-run --json`, `pipeline run --dry-run --json`, report-generator previews, login/logout previews, and `mcp-config --output <file> --dry-run --json-result`. CI runs
|
|
33
|
+
The 67-test TypeScript regression suite covers home-path expansion, custom NT data-root discovery, bounded daily-favorites synchronization, confirmed no-AI daily generation, no-AI Vault promotion, Vault content mutation previews, Vault initialization and synchronization confirmation, MCP configuration write confirmation, source/compiled Python resource lookup, outbound PII redaction, local-inference bypass, strict message-body masking, MCP path/date/URL validation and live tool discovery, message-contract preservation, local-date export bounds, export result metadata, evidence-package safety, assistant routing and lifecycle confirmation, message-channel authentication confirmation, todo and access-control mutation confirmation, message-send preview/confirmation, local-reader port validation, redacted configuration status, access-list JSON, database-key reset confirmation, human-gated initialization and key capture, content-free account-scan and assistant-log diagnostics, confirmed Vault RAG, confirmed semantic search and RAG chat, structured todo reminders, strict knowledge limits, report previews, and cover-image signature validation. Core automation entry points now include `capabilities --json`, `config show --json`, `whitelist list --json`, `blacklist list --json`, `check --json`, `init --dry-run --json`, `scan --json`, `dbkey --dry-run --json`, `sessions --json`, `messages --json`, `contacts --json`, `export ... --json`, `fav export ... --json-result`, `sns ... --json`, `daily-stats --json`, `daily --no-ai --dry-run --json`, `daily-server --status --json`, `assistant status --json`, `assistant log --json`, `todos list --json`, `todos remind --json`, `evidence --json`, `sync run --dry-run --json`, `sync run --yes --json`, `sync status --json`, `sync verify --json`, `evidence-review --dry-run --json`, Vault mutation previews, confirmed `vault rag`, confirmed `search` and `chat`, daily-favorite previews, `search-index --dry-run --json`, `pipeline run --dry-run --json`, report-generator previews, login/logout previews, and `mcp-config --output <file> --dry-run --json-result`. CI runs three jobs: `node` (build plus the TypeScript suite on Node 22.13), `python` (the Python suites on 3.11), and `release-consistency` (package.json and package-lock.json version agreement, plus a check that the npm package carries no outputs, models, databases or keys). The jobs are separate because a Node failure previously stopped the Python tests from running at all, which let them fail unnoticed.
|
|
32
34
|
|
|
33
35
|
The daily workflow supports `dailyAiEnabled=false` for a persistent no-AI mode, or `daily --no-ai` for a single run. Both the CLI and direct Python entry points honor the setting. Fetching, HTML generation, and local indexes remain available in that mode. Machine execution requires `daily --yes --json`; it keeps progress logs on stderr and returns a machine-readable completion result on stdout after checking the required local artifacts.
|
|
34
36
|
|
|
35
|
-
Date-bounded message exports and `wechat.export_messages` page through the selected conversation before applying the result limit. Older matching dates are therefore not hidden by a newer, nonmatching first page. Versioned envelopes report requested bounds, returned count, and conservative `mayHaveMore` metadata
|
|
37
|
+
Date-bounded message exports and `wechat.export_messages` page through the selected conversation before applying the result limit. Older matching dates are therefore not hidden by a newer, nonmatching first page. Versioned envelopes report requested bounds, returned count, and conservative `mayHaveMore` metadata. `sync` adds a local checkpoint on top of the same overlapping-window approach, but **a stable incremental cursor is still not exposed** - it must be supported by every relevant database backend before it is advertised (D-027), and `capabilities --json` reports `stableCursor: false` for both.
|
|
36
38
|
|
|
37
39
|
## Security Baseline
|
|
38
40
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# 同步与任务状态契约
|
|
2
|
+
|
|
3
|
+
本文冻结两个内部状态 schema:`weflow-sync/v1`(跨运行的同步检查点)与 `weflow-job/v1`
|
|
4
|
+
(单次运行的记录)。二者都在本机 `~/.weflow-cli/` 下,**不对外暴露**。
|
|
5
|
+
|
|
6
|
+
它们是 [FUNCTIONAL-IMPROVEMENT-SPEC.md](FUNCTIONAL-IMPROVEMENT-SPEC.md) §4 与 §5 的落地,
|
|
7
|
+
边界受 [DECISIONS.md](DECISIONS.md) 约束(尤其 D-014、D-019、D-025、D-027)。
|
|
8
|
+
|
|
9
|
+
## 为什么是两个文件
|
|
10
|
+
|
|
11
|
+
生命周期不同,不是猜测性抽象:
|
|
12
|
+
|
|
13
|
+
| 文件 | 生命周期 | 记录的是 |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `sync/<source>-<name>-<hash>.json` | **跨运行**,每次同步覆盖 | 已覆盖到哪、分片读没读全 |
|
|
16
|
+
| `jobs/<jobId>.json` | **单次运行** | 这次运行的起止、状态、结果 |
|
|
17
|
+
|
|
18
|
+
一次运行写一个 job 记录;同步检查点记录"到目前为止覆盖范围"这一长期事实。
|
|
19
|
+
|
|
20
|
+
## `weflow-sync/v1`
|
|
21
|
+
|
|
22
|
+
路径:`~/.weflow-cli/sync/<source>-<sanitize(talker)>-<sha256(talker)[0:8]>.json`
|
|
23
|
+
|
|
24
|
+
**按会话分文件**,不是一个全局文件。理由:全局文件要求跨会话读改写并合并,而分文件
|
|
25
|
+
让每次写入都是整文件原子替换;同时文件名里的 hash 后缀让 `wxid_a@chatroom` 与
|
|
26
|
+
`wxid_a_chatroom` 落到不同文件,而显示名不会出现在目录列表里。
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"schema": "weflow-sync/v1",
|
|
31
|
+
"schemaVersion": 1,
|
|
32
|
+
"source": "wechat-nt",
|
|
33
|
+
"scope": "<显示名,未知时等于 talker>",
|
|
34
|
+
"talker": "<会话标识,不是路径>",
|
|
35
|
+
"lastSuccessfulRun": "2026-09-19T09:00:00+08:00",
|
|
36
|
+
"lastAttempt": "2026-09-19T09:02:10+08:00",
|
|
37
|
+
"coveredFrom": "2026-09-01T00:00:00+08:00",
|
|
38
|
+
"coveredTo": "2026-09-19T08:59:59+08:00",
|
|
39
|
+
"checkpoint": { "newestCreateTime": 1789000000, "overlapSeconds": 300 },
|
|
40
|
+
"recordsRead": 1200,
|
|
41
|
+
"recordsDeduplicated": 14,
|
|
42
|
+
"recordsReturned": 1186,
|
|
43
|
+
"shardsRead": 4,
|
|
44
|
+
"shardsFailed": 0,
|
|
45
|
+
"shards": {
|
|
46
|
+
"scanned": 4, "opened": 4, "failed": 0,
|
|
47
|
+
"items": [
|
|
48
|
+
{ "name": "message_0.db", "opened": true, "hasTalkerTable": true,
|
|
49
|
+
"rowsForTalker": 8123, "reason": null }
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"coverage": "complete",
|
|
53
|
+
"mayHaveMore": false,
|
|
54
|
+
"warnings": [],
|
|
55
|
+
"updatedAt": "2026-09-19T09:02:10+08:00"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 两个时间戳必须分开
|
|
60
|
+
|
|
61
|
+
| 字段 | 何时推进 |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `lastAttempt` | **每次尝试都推进**,无论结果 |
|
|
64
|
+
| `lastSuccessfulRun` | **仅在 `coverage !== 'partial'` 时推进** |
|
|
65
|
+
|
|
66
|
+
只有一个的话,"跑了但部分完成"会和"跑成功了"混为一谈——而这正是方案 §5.2 明令禁止的
|
|
67
|
+
(部分完成不能返回成功状态)。
|
|
68
|
+
|
|
69
|
+
### `coverage` 三档
|
|
70
|
+
|
|
71
|
+
| 值 | 含义 |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `complete` | 扫到的分片全部打开、打开的全读成功,且 `mayHaveMore` 为 false |
|
|
74
|
+
| `unverified` | 有分片**打不开**,但打开的都读成功。已读部分是可信的,未打开的分片是否含本会话消息**无法判断** |
|
|
75
|
+
| `partial` | 某个**已打开**的分片读失败,或 `mayHaveMore` 为 true,或分页在到达窗口起点前就短页结束 |
|
|
76
|
+
|
|
77
|
+
`unverified` 不是失败:`connect_message_shards` 一直把打不开的分片当作可跳过的(这是有意设计),
|
|
78
|
+
CLI 无法知道那个分片里有没有本会话的消息。如实标成 `unverified` + `warnings` 比直接失败更有用,
|
|
79
|
+
也让自动化不至于卡死。
|
|
80
|
+
|
|
81
|
+
`partial` 则**必须失败**(`code: 'SYNC_PARTIAL'`,退出码 1):状态文件照写(方案 §5.2 要求
|
|
82
|
+
部分完成不得删除已生成的可用结果),但 `lastSuccessfulRun` 不推进。
|
|
83
|
+
|
|
84
|
+
### 身份
|
|
85
|
+
|
|
86
|
+
按方案 §4.2,`serverId` 非零时优先;**`localId` 绝不单独作为全局身份**(D-014 同此约束)。
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
talker|s:<serverId> serverId 非零
|
|
90
|
+
talker|l:<localId>|t:<createTime> 否则
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**与方案 §4.2 的一处有意偏离**:键里**不含 `shard`**。消息载荷里没有 shard 字段
|
|
94
|
+
(`nt_decrypt._message_dict` 不产出它),加进去要动 `export json` 的消息契约,而 D-016/D-027
|
|
95
|
+
把那条线封住了。它对本契约要解决的**重叠去重**也不必要——同一条消息重读两次,得到的
|
|
96
|
+
`serverId` 或 `(localId, createTime)` 本来就一样。只有跨分片的 `(localId, createTime)` 撞车
|
|
97
|
+
才需要它,而 `talker` + `createTime` 已让这种撞车极其遥远。**真观测到再加,不预先加。**
|
|
98
|
+
|
|
99
|
+
### 明确不做的事
|
|
100
|
+
|
|
101
|
+
**`mayHaveMore` 不是稳定游标。** 按 D-027,稳定增量游标必须在所有相关后端都支持后才能
|
|
102
|
+
对外宣称;在此之前只提供重叠时间窗 + 本地去重,由下游决定重复容忍度。
|
|
103
|
+
|
|
104
|
+
**不得写入**:数据库密钥、`x'<key><salt>'`、聊天正文、绝对路径(D-001 / D-002 / D-025)。
|
|
105
|
+
状态服务在写盘前统一过一遍脱敏,不指望每个调用方都记得。
|
|
106
|
+
|
|
107
|
+
## `weflow-job/v1`
|
|
108
|
+
|
|
109
|
+
路径:`~/.weflow-cli/jobs/<jobId>.json`。`jobId` 形如 `job_20260919_0001`。
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"schema": "weflow-job/v1",
|
|
114
|
+
"jobId": "job_20260919_0001",
|
|
115
|
+
"kind": "sync.messages",
|
|
116
|
+
"state": "running",
|
|
117
|
+
"createdAt": "2026-09-19T09:00:00+08:00",
|
|
118
|
+
"updatedAt": "2026-09-19T09:02:10+08:00",
|
|
119
|
+
"window": { "from": 1788000000, "to": 1789000000 },
|
|
120
|
+
"progress": { "completed": 800, "total": 1200 },
|
|
121
|
+
"resumeable": true,
|
|
122
|
+
"result": { "records": 800, "recordsDeduplicated": 14, "shardsFailed": 0 },
|
|
123
|
+
"error": null
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
状态机:`planned → waiting_confirmation → running → paused`,终态 `completed | partial | failed | cancelled`。
|
|
128
|
+
本轮只实现 `running | completed | partial | failed`,其余是预留(不实现的不写进状态机文档当既有能力)。
|
|
129
|
+
|
|
130
|
+
`partial` 是本项目新增的一档,**不算成功**。`error` 只放既有的大写错误码
|
|
131
|
+
(`NOT_INITIALIZED`、`CONFIRMATION_REQUIRED`、`INVALID_ARGUMENT`、`SYNC_PARTIAL`、
|
|
132
|
+
`DATABASE_CONNECTION_FAILED` 等),不写中文长句供机器解析。
|
|
133
|
+
|
|
134
|
+
**幂等**:同一范围重复提交应返回已有任务或明确新建,不得静默重复写入。本轮用
|
|
135
|
+
`kind + talker + 窗口` 生成幂等提示,命中未完成的同键任务时返回该任务而非新建。
|
|
136
|
+
|
|
137
|
+
> 本轮 `sync` 是 job 记录的唯一写入者。若日后认为不值得单独一个文件,
|
|
138
|
+
> **回退路径**:删掉 job 存储、把那个 `lastJob` 对象并入各会话的 `weflow-sync/v1` 文件。
|
|
139
|
+
> 因为此刻还没有第二个消费者,这是一次单文件改动,无兼容性影响。
|
|
140
|
+
|
|
141
|
+
## 版本升级策略
|
|
142
|
+
|
|
143
|
+
- `schema` 变化 = 不兼容变更,需要迁移说明。
|
|
144
|
+
- 只增字段 = 兼容变更;读方必须容忍未知字段(与 `DATA_CONTRACT.md` 对下游的要求一致)。
|
|
145
|
+
- 读到未知 `schema` 时**不得覆盖**:报明确错误并保留原文件。
|
|
146
|
+
|
|
147
|
+
## 相关文档
|
|
148
|
+
|
|
149
|
+
- [功能增强技术方案](FUNCTIONAL-IMPROVEMENT-SPEC.md)
|
|
150
|
+
- [消息数据契约](DATA_CONTRACT.md)
|
|
151
|
+
- [技术决策记录](DECISIONS.md)
|
|
152
|
+
- [项目当前状态](PROJECT_STATE.md)
|