@a9i5k4/dsh-auto-memory 3.0.0 → 3.1.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/README.md +30 -13
- package/README.zh-CN.md +30 -13
- package/docs/FRONTEND-CO-CREATION.md +191 -0
- package/docs/GM53-HOMEPAGE-PROMPT.md +323 -0
- package/docs/HANDBOOK.md +88 -52
- package/docs/HOMEPAGE-CONTENT-FOR-GM53.md +299 -0
- package/docs/PROMO-PROMPT-3.0.md +100 -0
- package/docs/USER-GUIDE.en.md +11 -11
- package/docs/USER-GUIDE.zh-CN.md +11 -11
- package/docs/WHITEPAPER.md +207 -0
- package/docs/screenshots/promo/promo-0-banner-v3.png +0 -0
- package/docs/screenshots/promo/promo-0-banner-v4.png +0 -0
- package/docs/screenshots/promo/promo-1b-auto-recall.png +0 -0
- package/lib/activation-host.js +69 -10
- package/lib/board-mode.js +1 -1
- package/lib/client.js +1697 -285
- package/lib/config-io.js +156 -0
- package/lib/context-bridge.js +3 -0
- package/lib/context-host.js +23 -10
- package/lib/degrade.js +385 -0
- package/lib/dsh-home.js +143 -0
- package/lib/episodic-store.js +142 -18
- package/lib/evidence-store.js +8 -1
- package/lib/fact-store.js +484 -43
- package/lib/hub-io.js +217 -0
- package/lib/index-sync.js +13 -1
- package/lib/index.js +1730 -202
- package/lib/intent-clean-safe.js +258 -40
- package/lib/l0-extract.js +231 -16
- package/lib/m4-corpus.js +8 -2
- package/lib/m7-index-sync-host.js +8 -1
- package/lib/memory-envelope.js +6 -1
- package/lib/memory-hub.js +164 -17
- package/lib/memory-index.js +4 -2
- package/lib/note-status-apply.js +118 -0
- package/lib/note-status.js +204 -0
- package/lib/procedure-store.js +333 -31
- package/lib/procedure-switch.js +38 -0
- package/lib/python-sidecar-client.js +314 -11
- package/lib/recall-fusion.js +83 -12
- package/lib/rules-edit.js +159 -0
- package/lib/semantic-decide.js +41 -8
- package/lib/semantic-js.js +51 -6
- package/lib/shadow-host.js +3 -5
- package/lib/skill-export-host.js +153 -0
- package/lib/skill-export.js +239 -0
- package/lib/storage-manage.js +6 -0
- package/lib/temporal-parse.js +191 -159
- package/lib/tier0-catalog.js +45 -3
- package/lib/wb-contract.js +198 -2
- package/lib/wb-sidecar.js +54 -3
- package/package.json +6 -2
- package/docs/internal/ACCEPT-35-LIVE.md +0 -143
- package/docs/internal/ACCEPTANCE-20260914.md +0 -90
- package/docs/internal/ARCH-REVIEW-BRIEF.md +0 -411
- package/docs/internal/ARCH-REVIEW-REQUEST.md +0 -201
- package/docs/internal/ARCH-REVIEW-ROUND2.md +0 -169
- package/docs/internal/ARCH-REVIEW-ROUND3.md +0 -206
- package/docs/internal/ART-DIRECTION-WIREFRAME.md +0 -181
- package/docs/internal/AUDIT-WB-GRAPH-FULL-20260916.md +0 -314
- package/docs/internal/CONCURRENCY-INVESTIGATION-20260917.md +0 -192
- package/docs/internal/CROSS-SESSION-SEARCH-PATH-DECISION.md +0 -72
- package/docs/internal/CROSS-SESSION-SEARCH-RESEARCH.md +0 -131
- package/docs/internal/CUA-VISION-FIX-NOTES.md +0 -78
- package/docs/internal/DECISIONS-20260914-SESSION.md +0 -269
- package/docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md +0 -292
- package/docs/internal/DESIGN-P1-STATE-COMMIT-20260915.md +0 -219
- package/docs/internal/DIRECTION-CHECK-WB-GRAPH-20260916.md +0 -132
- package/docs/internal/FEEDBACK-TO-DSHAPI-RELAY.md +0 -13
- package/docs/internal/GH-DISCUSSION-5732-COMMENT.md +0 -74
- package/docs/internal/GPT-ACCEPTANCE-PROMPT-20260916.md +0 -352
- package/docs/internal/GPT-REVIEW-PROMPT.md +0 -216
- package/docs/internal/GROUP-DIGEST-SETUP.md +0 -62
- package/docs/internal/GROUP-LISTENER-SETUP.md +0 -49
- package/docs/internal/GROUP-WEBHOOK-SETUP.md +0 -93
- package/docs/internal/HANDOFF-TO-ZCODE.md +0 -168
- package/docs/internal/KICKOFF-P0.md +0 -254
- package/docs/internal/MASTER-PLAN-3.0.md +0 -411
- package/docs/internal/MEMORY-MUTATION-AND-INDEX-DESIGN.md +0 -85
- package/docs/internal/MERGE-CONFLICT-SCAN-20260914.md +0 -222
- package/docs/internal/NEXT-VERSION-TODO.md +0 -95
- package/docs/internal/OFFICIAL-DISCUSSION-DRAFT.md +0 -80
- package/docs/internal/PENDING-FIXES-20260916.md +0 -289
- package/docs/internal/RAG-KARPATHY-PROGRAM.md +0 -229
- package/docs/internal/RELEASE-PROCESS.md +0 -99
- package/docs/internal/REPORT-P0-NIGHTLY.md +0 -212
- package/docs/internal/REPORT-P5-ACCEPTANCE.md +0 -31
- package/docs/internal/REPORT-WB-GRAPH-NIGHTLY.md +0 -153
- package/docs/internal/REVIEW-WB-GRAPH-SELF.md +0 -81
- package/docs/internal/ROADMAP-20260917-WEEK.md +0 -305
- package/docs/internal/ROADMAP.md +0 -106
- package/docs/internal/RUN-P0-NIGHTLY.md +0 -227
- package/docs/internal/S10-CONSTRUCTION-HANDOFF-20260917.md +0 -175
- package/docs/internal/S10-GAPS-PLAIN-20260917.md +0 -125
- package/docs/internal/SEMANTIC-ARCHITECTURE-SPEC.md +0 -360
- package/docs/internal/SESSION-FILE-REPAIR-PROTOCOL.md +0 -90
- package/docs/internal/SUBAGENT-REPORT-ROUTING-PRE-RESEARCH.md +0 -261
- package/docs/internal/THREE-LAYER-CONTRACT.md +0 -210
- package/docs/internal/TODO-BACKLOG.md +0 -263
- package/docs/internal/TODO-GRAPH.html +0 -715
- package/docs/internal/TODO-GRAPH.html.bak-20260914-v2 +0 -493
- package/docs/internal/TODO-GRAPH.html.bak-20260915-alsfix +0 -710
- package/docs/internal/TODO-GRAPH.html.bak-20260915-p1 +0 -710
- package/docs/internal/TODO-GRAPH.html.bak-20260915-p6a-rev +0 -703
- package/docs/internal/TODO-GRAPH.html.bak-20260915-wshint +0 -710
- package/docs/internal/TODO-GRAPH.html.bak-20260916-batch +0 -715
- package/docs/internal/WB-FORMAT-CONVENTION.md +0 -112
- package/docs/internal/WB-GRAPH-DECISIONS-20260914.md +0 -71
- package/docs/internal/WB-GRAPH-INTEGRATION-PLAN.md +0 -386
- package/docs/internal/WB-GRAPH-RESEARCH-BRIEF.md +0 -118
- package/docs/internal/WB-GRAPH-RESEARCH-EXTERNAL.md +0 -228
- package/docs/internal/WB-GRAPH-RESEARCH-LOCAL.md +0 -190
- package/docs/internal/reviews/CLAIM-VERIFICATION-20260914.md +0 -56
- package/docs/internal/reviews/PLAN-gpt6astra-round2-20260914.md +0 -787
- package/docs/internal/reviews/REVIEW-gpt6astra-20260914.md +0 -112
- package/docs/internal/reviews/ROUND3-REVIEW-INTEGRATION-20260914.md +0 -230
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
# P1 设计稿 · 统一状态提交与快照(miv 单源)+ 并发原子边界
|
|
2
|
-
|
|
3
|
-
> 状态:**待审**(先审后写代码 —— 用户裁定顺序:设计稿是必审点)
|
|
4
|
-
> 依据:`MASTER-PLAN-3.0.md` Phase 1(§216-227)、`TODO-GRAPH.html` 卡 `V2-P1`、`ROUND3-REVIEW-INTEGRATION-20260914.md` §3.2
|
|
5
|
-
> 作者:执行方 · 日期:2026-09-15 · 全部 file:line 为**本机实测**取值,非转述
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 0. 一句话目标
|
|
10
|
-
|
|
11
|
-
把「谁在写、写的什么版本、写完算不算数」收敛成**一个提交边界**:所有窗口经同一工作区 owner 提交,
|
|
12
|
-
提交时在边界**内**校验 `expectedDigest` 与 `miv`;快照的 `miv` 成为**唯一事实源**(内容身份 + 状态清单摘要,
|
|
13
|
-
哈希身份、不递增、不比较)。并发不加锁,靠「队列串行 + 边界内比较」保证恰好一项成功、另一项收到**可见冲突**。
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 1. 现码取证(每条都可在本机复现)
|
|
18
|
-
|
|
19
|
-
### 1.1 已有的好东西(**不要重造**)
|
|
20
|
-
|
|
21
|
-
| 机制 | 位置 | 实测语义 |
|
|
22
|
-
|---|---|---|
|
|
23
|
-
| 短提交队列 | `lib/memory-writer-pre.js:235 _queue` | 按 `path.resolve(filePath)` 分键串行;空闲即回收 Map 条目(`:242`),无界增长已处理 |
|
|
24
|
-
| 原子写 | `lib/memory-writer-pre.js:199 atomicReplace` | 同目录 tmp + fsync + rename。**签名里没有 `expectedDigest`** —— 它**不是** CAS,只做原子写 |
|
|
25
|
-
| 边界内 digest 校验 | `lib/memory-writer-pre.js:344 replace` / `:332` / `:357` / `:369` / `:380` | 五个提交方法**都在 `_queue` 内部**先比 `expectedDigest`,不匹配返回 `{ok:false, reason:…}`;**这是当前唯一正确的并发保护** |
|
|
26
|
-
| 无 BOM 闸 | `lib/memory-writer-pre.js:287` | 提交前拒 BOM(用户硬性规则的代码级保障) |
|
|
27
|
-
| 写入门 | `lib/memory-mutation-pre.js:76 validateMutationBoundaryPre` | 只收规范化投影(`beforeIds`/`afterIds`/`archivedIds`);`M1 丢卡保护`无条件生效(`:99-101`) |
|
|
28
|
-
| 门调用点 | `lib/index.js:1984` | **全仓唯一实调**(`memory-mutation-pre.js:231` 是自身内部调用) |
|
|
29
|
-
|
|
30
|
-
### 1.2 缺口(P1 要修的)
|
|
31
|
-
|
|
32
|
-
**(a) `miv` 有生成、无单源,且**没有被提交边界校验****
|
|
33
|
-
|
|
34
|
-
- 生成:`lib/shadow-retrieval-pre.js:144` —— `idx_pre_ + first32hex(sha256(canonical corpus tuples))`;经 `lib/m4-corpus-pre.js:128` → `context-host-pre.js:216/393` 装配进快照。
|
|
35
|
-
- 消费:`lib/activation-host-pre.js:102 setMiv` / `:252 currentMiv` 只做**缓存**(`mivCache = {wsRef, miv}`),`setMiv` 是**显式写入口**(`:111`)——即"谁调谁说了算",**没有单一权威计算点**。
|
|
36
|
-
- **关键缺口**:`memory-writer-pre.js` 的提交方法**只比 `expectedDigest`,完全不看 `miv`**。⇒ 图内容变了但 digest 恰好相等(或调用方没传 digest)时,快照仍可能带着**旧 miv** 发出去。
|
|
37
|
-
|
|
38
|
-
**(b) ~~复用命中只查 `contextVersion`,不查 `miv` ⇒ 契约 I6 失守~~ —— ★施工期核实:本条已过时,撤销**
|
|
39
|
-
|
|
40
|
-
> **勘误(实测取证,非推断)**:以下三行是**当时**(09-14 评审 C9 口径)的判断;动手前逐行复核,现状已是:
|
|
41
|
-
> - 投影**已携带** miv —— `lib/activation-host-pre.js:165`:`miv: String(req.memoryIndexVersion || '') || undefined`(同处 `:164/166/167` 带 `contextVersion`/`observationId`/`requestKey`);
|
|
42
|
-
> - 版本门**已含 miv 两道 fail-closed** —— `lib/tier-layer-inject-pre.js:200-204`:`if (!curMiv || !projMiv) return no('version-unknown')` / `if (curMiv !== projMiv) return no('miv-changed')`;
|
|
43
|
-
> - 调用方**已传当前 miv** —— `lib/index.js:4269`:`miv: this.tierCurrentMivPre()`;
|
|
44
|
-
> - **且有专项测试** —— `tests/smoke/smoke-test-t0-2-version-gate-pre.mjs:75`(`miv-changed`)、`:96/:103`(`version-unknown` 两侧)。
|
|
45
|
-
>
|
|
46
|
-
> **结论**:I6 在 P1 开工前**已由 P0 的 T0-2 守住**。§3 表中「I6 失守 → 守住」一栏作废;原 §2.4(版本门补 miv)**从改动清单撤销** —— 重复实现只会引入回归风险。
|
|
47
|
-
> **教训**:外部评审结论带时间戳,动手前必须用**当前**代码复核,不得把评审描述当现状直接排进施工项。
|
|
48
|
-
|
|
49
|
-
*(以下为当时的分析原文,仅作留痕,勿据此施工)*
|
|
50
|
-
|
|
51
|
-
- 契约原文 `docs/internal/THREE-LAYER-CONTRACT.md:184`:**I6** 三层来自同一份快照(同一 `miv`),混版视为错误。
|
|
52
|
-
- 实测 `lib/tier-layer-inject-pre.js:169 selectReusableTierHitsPre`:① 时间门(`:186`)② 身份门 session/workspace(`:188-194`)③ **版本门只有 `contextVersion`**(`:195-198`)。**`gh.miv` 虽被读入快照字段(`:177`)却不参与判定** ⇒ 语料换了、miv 变了,只要 contextVersion 没变就**照样复用旧命中**,三层可能混版。
|
|
53
|
-
- 这与我方 09-14 外部评审 **C9** 结论一致(`lib/index.js:3894/3896/3901` 复用命中只查时间与会话)。
|
|
54
|
-
|
|
55
|
-
**(c) 提交接口没有来源身份**
|
|
56
|
-
|
|
57
|
-
- 现签名(`memory-writer-pre.js:344`)只收 `expectedDigest` + `replacement` + `idFactory`。
|
|
58
|
-
- 缺 `workspaceKey / boardId / txId / actor` ⇒ 冲突被拒时**说不出"谁和谁撞了"**,`mutationRefusalTextPre`(`:212`)也拿不到 actor 信息。
|
|
59
|
-
|
|
60
|
-
**(d) 共享 vs 隔离虽已分开,但缺显式声明**
|
|
61
|
-
|
|
62
|
-
- 图(共享):按工作区分键(`_queue` 的 key 是文件路径;工作区文件路径天然隔离)✓
|
|
63
|
-
- 激活/冷却/已交付标记(隔离):`lib/activation-host-pre.js` 的 `registry.forRuntime(sessionId, workspaceKey, …)`(`:132/248`)已按会话隔离 ✓
|
|
64
|
-
- **缺**:这个"哪些共享、哪些隔离"的边界目前只存在于代码直觉里,没有写成常量表 ⇒ 后续 P6B 容易越界。
|
|
65
|
-
|
|
66
|
-
**(e) 三种状态目前无类型隔离**
|
|
67
|
-
|
|
68
|
-
- 记忆有效状态:`current / superseded / retracted`(契约 I5,`index.js` 注入侧过滤)
|
|
69
|
-
- 任务进度:`done / passed / archived`(看板语义)
|
|
70
|
-
- 归档位置:`archived`(同时也是任务态 —— **同名不同义**)
|
|
71
|
-
- 风险:`archived` 一词两义,跨线传递时会误判。P1 只做**命名隔离**(见 §2.3),不做语义合并。
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## 2. 改动清单(按依赖序,每步可独立回归)
|
|
76
|
-
|
|
77
|
-
### 2.1 【新增】`lib/state-commit-pre.js` —— 统一提交契约(纯函数,零依赖)
|
|
78
|
-
|
|
79
|
-
```js
|
|
80
|
-
export const STATE_COMMIT_VERSION_PRE = 'state_commit_pre_v1'
|
|
81
|
-
|
|
82
|
-
// 提交单据(P1 唯一的写入契约)
|
|
83
|
-
// { workspaceKey, boardId, txId, expectedDigest, expectedStateVersion,
|
|
84
|
-
// actor: { sessionId, contSeq, kind }, // kind: 'user' | 'plugin' | 'model' | 'system'
|
|
85
|
-
// writes: [{ path, content, expectedDigest }],
|
|
86
|
-
// stateChanges: [{ target, from, to }] }
|
|
87
|
-
|
|
88
|
-
export function buildStateCommitPre(input) // 归一化 + 必填校验,缺字段 fail-closed
|
|
89
|
-
export function commitConflictPre(commit, observed) // → { ok:false, reason, detail:{ expected, observed, actor, boardId } }
|
|
90
|
-
export function commitReceiptPre(commit, result) // → { txId, boardId, miv, digest, at, actor }
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**`boardId` 硬约束(卡内明确要求)**:在工作区内**稳定**、**不含当前会话号**
|
|
94
|
-
⇒ 由 `sha256(workspaceKey + '|' + scope)` 派生,**不得**掺 `sessionId`。理由:接续后新窗口换 sessionId,
|
|
95
|
-
若 boardId 跟着变,图就会被当成两块,共享语义直接崩。
|
|
96
|
-
|
|
97
|
-
### 2.2 【新增】`miv` 单源:`memoryIndexVersionPre(projection)`
|
|
98
|
-
|
|
99
|
-
> **★2026-09-15 施工期核实:§6 第 2 步「收敛 `activation-host-pre.js:111 setMiv`」已撤销。**
|
|
100
|
-
> 实测 `setMiv`(`lib/activation-host-pre.js:111` + 孪生 `lib/activation-host.js:111`)**全仓零调用者**(`lib/`、`tests/`、`lib/client.js` 全量检索仅命中其自身定义行)⇒ 它是**死代码**,
|
|
101
|
-
> **没有"活跃写者"需要收敛**。真实 miv 更新路径是 `:100-104` 的 `corpusRegistry.get(catalog)` 分支(`mivCache = { wsRef: ws, miv: res.snapshot.memoryIndexVersion }`),
|
|
102
|
-
> 而该 `memoryIndexVersion` 来自契约 §8 的 `shadow-retrieval-pre.js:145 memoryIndexVersion(sources)` —— **它才是建索引侧真源**。
|
|
103
|
-
> 结论:P1 **不删死代码、不动 `mivCache`**(删死代码属独立清理项,不混进 P1;动了反而扩大回归面)。
|
|
104
|
-
|
|
105
|
-
```js
|
|
106
|
-
// lib/state-commit-pre.js
|
|
107
|
-
export function memoryIndexVersionPre(projection) {
|
|
108
|
-
// 输入:{ records:[{id, status, l0?}], boardCards?:[{id, status}], scope }
|
|
109
|
-
// 输出:'idx_pre_' + first32hex(sha256(canonical))
|
|
110
|
-
// canonical = 按 id 升序的 [id, status, contentDigest] 三元组(换行拼接,无尾随空白)
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**三条硬规则**(卡内 T1-5 扩展口径):
|
|
115
|
-
|
|
116
|
-
1. **内容身份 + 状态清单摘要**:`status` 进摘要 ⇒ 仅状态变化(`current→superseded`)**miv 必变**。
|
|
117
|
-
2. **不递增、不比较**:miv 是哈希身份,**不是版本序**。任何代码不得写 `if (miv > lastMiv)`。
|
|
118
|
-
3. **白板卡片状态也进摘要**:`boardCards` 参与 canonical ⇒ 扩到白板。
|
|
119
|
-
|
|
120
|
-
**必须废弃的口径**:WB-GRAPH 的 `index.json` 里 `rebuilt_at` **不得**当版本序。它是时间戳,重建时间变但内容没变时它会变 ⇒ 用它当版本序会造成**假失效 + 真混版**。P1 交付物里包含一条断言专门钉死它。
|
|
121
|
-
|
|
122
|
-
### 2.3 【改动】`memory-writer-pre.js`:提交方法收 `expectedStateVersion` + actor
|
|
123
|
-
|
|
124
|
-
- `replace` / `append` / `migrate` 等五个方法(`:332/344/357/369/380`)在 `_queue` **内部**、`expectedDigest` 校验**之后**,追加:
|
|
125
|
-
```
|
|
126
|
-
if (opts.expectedStateVersion != null && state.stateVersion !== opts.expectedStateVersion)
|
|
127
|
-
return commitConflictPre(...) // 可见冲突,不写
|
|
128
|
-
```
|
|
129
|
-
- **不改 `atomicReplace` 签名**(它不是 CAS,别给它加语义)。
|
|
130
|
-
- **不引入长期编辑锁、不按会话分片**(卡内明确否决)。队列 key 仍是文件路径。
|
|
131
|
-
|
|
132
|
-
### 2.4 【撤销】~~`tier-layer-inject-pre.js:195` 版本门补 `miv`~~
|
|
133
|
-
|
|
134
|
-
**★2026-09-15 施工期核实后撤销**:该检查**已存在**(`:200-204`,含 `version-unknown` 与 `miv-changed` 两条 fail-closed),
|
|
135
|
-
投影与调用方亦已就位(`activation-host-pre.js:165` / `index.js:4269`),并有专项测试
|
|
136
|
-
`tests/smoke/smoke-test-t0-2-version-gate-pre.mjs`。**I6 已守住,此处不改任何代码。**
|
|
137
|
-
|
|
138
|
-
*(以下原始改动设想作废,仅留痕)*
|
|
139
|
-
- ~~现:只比 `contextVersion`。改:`contextVersion` 且 `miv` 两侧可得且相等;任一侧缺 `miv` ⇒ 不复用。~~
|
|
140
|
-
- ~~新增拒绝原因 `miv-mismatch` / `miv-unknown`~~ ⇒ 既有实现用的是 **`version-unknown`** 与 **`miv-changed`**(`tier-layer-inject-pre.js:200-204`),命名不同因而已有测试可依。
|
|
141
|
-
|
|
142
|
-
### 2.5 【新增】状态命名隔离常量
|
|
143
|
-
|
|
144
|
-
```js
|
|
145
|
-
export const MEMORY_STATUS_PRE = Object.freeze(['current', 'superseded', 'retracted'])
|
|
146
|
-
export const TASK_STATE_PRE = Object.freeze(['open', 'done', 'passed'])
|
|
147
|
-
export const ARCHIVE_STATE_PRE = Object.freeze(['active', 'archived'])
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
三者**不得互转**(卡内"三种状态不能混")。`archived` 一词两义的问题以 `ARCHIVE_STATE_PRE` 独立命名解决,
|
|
151
|
-
**映射由白板线适配器负责**(KICKOFF §3.4 边界约定:3.0 不自行解释图格式)。
|
|
152
|
-
|
|
153
|
-
### 2.6 【改动】冲突可见性
|
|
154
|
-
|
|
155
|
-
- `mutationRefusalTextPre`(`memory-mutation-pre.js:212`)扩字段:拒绝文本必须带**当前版本 + 冲突目标**(卡内 T1-7C 要求),形如
|
|
156
|
-
`[提交被拒] tx=<txId> 目标=<path> 期望 miv=<a> 实测 miv=<b> 冲突方=<actor.sessionId>@<contSeq>`
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## 3. 契约影响
|
|
161
|
-
|
|
162
|
-
| 契约 | 现状 | P1 后 | 是否接口改动 |
|
|
163
|
-
|---|---|---|---|
|
|
164
|
-
| **I5** 非 current 两处过滤 | P0 已达标(注入侧 import 检索侧 `isCurrentPre`) | 不变 | 否 |
|
|
165
|
-
| **I6** 三层同快照(同一 miv) | **已守住**(P0 的 T0-2 已修,见 §1.2b 勘误) | **不变**(P1 不动此路径) | 否 |
|
|
166
|
-
| **I7** 降级必须显式标注 | 达标 | 新增 `miv-unknown` 降级行 | 否(复用既有降级通道 `index.js:4246`) |
|
|
167
|
-
| T1-5 仅状态变化 miv 必变 | 未覆盖白板 | 覆盖(§2.2 规则 3) | 否 |
|
|
168
|
-
| `validateMutationBoundaryPre` | 只收投影 | **不变**(3.0 只接收规范化投影,不解释图格式) | 否 |
|
|
169
|
-
|
|
170
|
-
**兼容档**:~~`memoryMutationMode='readonly'`(既有开关)~~ **★2026-09-15 施工期核实:该键在代码中不存在**
|
|
171
|
-
(`lib/`、`tests/`、`lib/client.js`、本机 `~/.dsh/dsh-auto-memory-pre.json` 全量检索均无命中;仅出现在 4 份文档里:
|
|
172
|
-
`PLAN-gpt6astra-round2-20260914.md:458/696`、`MASTER-PLAN-3.0.md:227`、本稿)。
|
|
173
|
-
⇒ **P1 的实际回滚面 = 新增字段全部可选**:不传 `expectedStateVersion`(也不传 `expectedDigest`)时,
|
|
174
|
-
`_checkCommitBoundary` 两个闸都不触发,行为与 P1 前**逐字节一致** —— 这是已实测的(见 T1-8)。
|
|
175
|
-
`readonly` 若要落地,属**独立新功能**(需实现 + 配置项 + GUI),**不在 P1 范围**,另行排期。
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
## 4. 验收断言(**能失败**,逐条对应卡片 crit)
|
|
180
|
-
|
|
181
|
-
| ID | 断言 | 对应 crit |
|
|
182
|
-
|---|---|---|
|
|
183
|
-
| **T1-1** | 两窗口读同一 digest 后**同时**提交替换 → 恰好一项 `ok:true`,另一项 `ok:false` 且 `reason` 可见 | 卡内 T1-4 |
|
|
184
|
-
| **T1-2** | 成功版本内容**未被覆盖**(终态 == 成功者写入内容) | 卡内 T1-4 |
|
|
185
|
-
| **T1-3** | A、B 会话共享图节点;**A 的激活包与交付记录不出现在 B** | 卡内 T1-7B |
|
|
186
|
-
| **T1-4** | 旧接续窗口迟到写入**不能覆盖新图**;拒绝信息带**当前版本 + 冲突目标** | 卡内 T1-7C |
|
|
187
|
-
| **T1-5a** | 业务内容改变 ⇒ miv 变 | 卡内 T1-5 |
|
|
188
|
-
| **T1-5b** | **仅**状态变化(`current→superseded`)⇒ miv **必变** | 卡内 T1-5 |
|
|
189
|
-
| **T1-5c** | 仅切换会话、仅更新 `rebuilt_at` ⇒ miv **不变** | 卡内 T1-5 |
|
|
190
|
-
| **T1-5d** | 白板卡片状态变化 ⇒ miv **必变** | Phase 1 扩展 |
|
|
191
|
-
| **T1-6** | `boardId` 不含 sessionId:同一工作区两个不同 sessionId ⇒ `boardId` 相等 | 卡内接口要求 |
|
|
192
|
-
| **T1-7** | 复用命中 `miv` 不等 ⇒ **不复用**(三层不混版);任一侧缺 miv ⇒ 不复用 | 卡内 T1-5 / I6 |
|
|
193
|
-
| **T1-8** | `expectedStateVersion` 缺省 ⇒ 行为与 P1 前**逐字节一致**(回归守卫) | 兼容档 |
|
|
194
|
-
| **T1-9** | ~~`memoryMutationMode='readonly'` ⇒ 所有提交被拒~~ **★核实:该键不存在,本条改写为兼容档真值表**:① 不传 `expectedStateVersion` ⇒ 提交照常成功(零行为变化)② 传入且匹配 ⇒ 成功 ③ 传入且不符 ⇒ 拒绝且可见 | 兼容档 |
|
|
195
|
-
|
|
196
|
-
**注入式沙箱纪律**(本项目已踩 3 次):新方法被抽进 `new Function` 沙箱时,
|
|
197
|
-
其引用的**所有**模块级符号必须显式列入 helpers,否则 `ReferenceError` 会被外层 `catch` 吞成"静默空内容"。
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
## 5. 明确**不做**的事(防止范围蔓延)
|
|
202
|
-
|
|
203
|
-
- ✗ 不加长期编辑锁、不按会话分片(卡内否决)
|
|
204
|
-
- ✗ 不改 `atomicReplace` 语义(它不是 CAS)
|
|
205
|
-
- ✗ 不引入 `miv` 序比较(哈希身份,不递增)
|
|
206
|
-
- ✗ 不在 3.0 内解析白板格式(KICKOFF §3.4:白板线拥有 `parseWhiteboardPre`)
|
|
207
|
-
- ✗ 多宿主同目录 —— **留给 U2 单独认证**;若要多进程直写同一文件,必须另加跨进程原子提交或单写者路由
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## 6. 施工顺序(每步跑全量回归)
|
|
212
|
-
|
|
213
|
-
1. `state-commit-pre.js` + 单测(纯函数,无接入风险)
|
|
214
|
-
2. ~~`miv` 单源接线(`setMiv` 收敛到单一计算点)+ T1-5a/b/c/d~~ **已撤销**(见 §2.2 勘误:`setMiv` 为死代码,无对象可收敛)+ T1-5a/b/c/d ✅ **已完成**(`smoke-test-state-commit-pre.mjs`)
|
|
215
|
-
3. 队列内 `expectedStateVersion` 校验 + T1-1/T1-2/T1-4/T1-8/T1-9
|
|
216
|
-
4. ~~`tier-layer-inject-pre.js` 版本门补 miv + T1-7(修 I6)~~ **已撤销**(I6 由 P0 的 T0-2 守住,见 §2.4)
|
|
217
|
-
5. 状态命名隔离 + T1-3
|
|
218
|
-
6. 冲突可见性(拒绝文本)+ T1-4
|
|
219
|
-
7. 全量回归 + 更新 `TODO-GRAPH.html` V2-P1 卡
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# 方向核对清单 · WB-GRAPH 白板线(2026-09-16)
|
|
2
|
-
|
|
3
|
-
> **用途**:任务二材料。本文件是**给 GPT 及协作方核对"答题方向"**的清单——不是让 GPT 重新找 bug(那部分见 `GPT-REVIEW-PROMPT.md`),而是**确认修复方向是否正确、优先级是否合理、有没有更该做的事**。
|
|
4
|
-
> **前置**:任务一全量粗检已完成,结论见 `AUDIT-WB-GRAPH-FULL-20260916.md`。
|
|
5
|
-
> **当前状态**:**尚未动手修**。以下每一项都需要"确认/否决/改方向"三类裁决之一。
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 一、给 GPT 的三句话背景
|
|
10
|
-
|
|
11
|
-
1. 这个插件(`dsh-auto-memory`)已在 npm 有真实用户(年下载约 10,900),**legacy 档必须字节级不变**是硬约束——任何修复都不能让老用户行为漂移。
|
|
12
|
-
2. 白板线(WB-GRAPH)是"新功能 + 一键开关回退",开关默认 `legacy`。用户拍板过"一口气全做完,但线先别急着接"。
|
|
13
|
-
3. 15 日下午→16 日凌晨这一批改动**不够严谨**:新代码全在闸门后、回归跑不到,于是 **PASS 95/0/0 完全掩盖了 4 条致命缺陷**。这次核对的核心问题是:**修复方向对不对**。
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 二、需要裁决的方向性问题(共 6 条)
|
|
18
|
-
|
|
19
|
-
### Q1 ★ 注册闸门的修法:三选一
|
|
20
|
-
|
|
21
|
-
**问题**:`lib/index.js:7633` 是 `export function apply(ctx, config)`(**非 async**),而工具数组在 `apply()` 内**同步**构建(`:8506`),此刻 `engine.config` 仍是 `DEFAULT_CONFIG`(`boardMode:'legacy'`)⇒ 注册闸门**恒假**。
|
|
22
|
-
|
|
23
|
-
| 方案 | 做法 | 优点 | 风险 |
|
|
24
|
-
| --- | --- | --- | --- |
|
|
25
|
-
| A1-a | 工具无条件注册,闸门降到 **execute 内** | 改动最小 | legacy 档工具数变 16 ⇒ **违反"legacy 字节级不变"**,模型能看到不可用工具 |
|
|
26
|
-
| **A1-b(推荐)** | 新增**同步**读取器 `loadConfigSync()`(`readFileSync`+同合并逻辑),在 tools 构建前调用一次 | 保持 async 语义不动;启动期拿到真配置;闸门仍在注册层 ⇒ legacy 工具数仍 14 | 需保证 `loadConfigSync` 与 `loadConfig` **共用合并逻辑**,否则双源漂移 |
|
|
27
|
-
| A1-c | `apply` 改 `export async function apply` | 最"正统" | **需先确认 cordis 是否 await 插件 apply 的返回值**——未证实,风险最高 |
|
|
28
|
-
|
|
29
|
-
**请裁决**:A1-b 是否可行?有无第四条路(如 cordis 的 `inject`/配置注入机制本该提供启动期配置)?
|
|
30
|
-
|
|
31
|
-
> 附:本项目此前已把「工具注册须先 `await loadConfig()`」写进项目约定——**该约定本身表述有误**(apply 非 async),本次须一并更正为「启动期需要真配置时必须用同步读取器」。
|
|
32
|
-
|
|
33
|
-
### Q2 ★ 修复顺序:先改代码还是先补测试?
|
|
34
|
-
|
|
35
|
-
**问题**:4 条致命里,**BUG-10(id 不一致)**和 **BUG-1(注册闸门)**都能被一条新测试稳定抓住。当前 20 个套件里没有一个测过 "graph 档端到端"。
|
|
36
|
-
|
|
37
|
-
**两种顺序**:
|
|
38
|
-
- **(a) 先补 graph 档端到端套件(红)→ 再修**(TDD 式,修复可信度高,但先要造出能过 `apply()` 的测试夹具)
|
|
39
|
-
- **(b) 先修 → 再补套件**(快,但容易重蹈"改完不知道有没有真修好")
|
|
40
|
-
|
|
41
|
-
**请裁决**:推荐 (a) 还是 (b)?若 (a),`apply()` 的测试夹具成本是否可控(现有 20 个套件已能调 `apply(`,说明夹具模式已存在)?
|
|
42
|
-
|
|
43
|
-
### Q3 ★ 规划里 5 项"未做"(P2-3/P2-4/P2-5/P3-2/P3-3)是否本批一律补齐?
|
|
44
|
-
|
|
45
|
-
**背景**:这 5 项在 `WB-GRAPH-INTEGRATION-PLAN.md §5` 里**明确列了**,但 16 日凌晨没做。其中:
|
|
46
|
-
- **P3-3(三处工具数硬锁 14→16)被规划标为"必须项"**——不补则 graph 档测试必红。
|
|
47
|
-
- **P2-5(GUI 放行 `.json`)** 影响用户能否在面板里看到 `index.json`。
|
|
48
|
-
- **P2-3/P2-4** 是检索端与注入端的增强,**不做也不影响"能用"**,只是没兑现规划承诺。
|
|
49
|
-
- **P3-2** 只是一句 guide 文案。
|
|
50
|
-
|
|
51
|
-
**请裁决**:是"全补齐(严格按规划)",还是"先补齐 ①必须项(P3-3)+ ②让功能真正可用的(P2-5),其余(P2-3/P2-4/P3-2)另开批次"?
|
|
52
|
-
|
|
53
|
-
### Q4 ★ 用户的核心诉求"实时可视化看板"(dsh-graph 接线)该不该在本批做?
|
|
54
|
-
|
|
55
|
-
**背景**:`vendor/dsh-graph/` 已搬入(MIT,含 NOTICE-VENDOR.md),但**cordis 接线未做**——因为要改**用户 profile 配置**(`~/.dsh/profiles/web/`),属高风险动作,需要先备份 + 用户明确同意。
|
|
56
|
-
|
|
57
|
-
**关键事实**:**即使前面 9 条全修完,只要 dsh-graph 不接线,用户依然"看不到看板"**——他最初的抱怨就没有真正解决。
|
|
58
|
-
|
|
59
|
-
**请裁决**:是
|
|
60
|
-
- (a) 本批就把 profile 接线做了(先备份,用户同意后执行),还是
|
|
61
|
-
- (b) 先把 memory_* 侧修干净、接线另立一步(用户可先验证 sidecar/遍历工具是否好用)?
|
|
62
|
-
|
|
63
|
-
### Q5 ★ "白板锚点未写入 Markdown"算不算规划未兑现?
|
|
64
|
-
|
|
65
|
-
**背景**:`WB-FORMAT-CONVENTION §2` 承诺的收益是"**白板内容凭锚点自动进入检索语料**(L0 抽取按锚点切条),无需任何新机制"。实测:`wbEntryIdPre` 只把 `mem_<32hex>` 写进 **sidecar 的 id 字段**,**从未写入 `PLAN.md`/账本的 Markdown 正文**(`WB-FORMAT-CONVENTION §2` 要求标题行下方紧跟 `<!-- memory:mem_... -->`)。
|
|
66
|
-
|
|
67
|
-
⇒ **该收益目前为零**:白板内容不会因锚点进检索。
|
|
68
|
-
|
|
69
|
-
**请裁决**:这是"实现 bug(应补写锚点到 Markdown)"还是"可以延期(先让 sidecar 跑通)"?若补写,会改动用户可见的 Markdown 内容,需确认是否可接受。
|
|
70
|
-
|
|
71
|
-
### Q6 ★ 交付报告的高报要不要更正留痕?
|
|
72
|
-
|
|
73
|
-
**背景**:`REPORT-WB-GRAPH-NIGHTLY.md` 把 `events.jsonl`、遍历工具返回正文、dsh-graph 接线、工具数硬锁都标 ✅,实际均未完成。
|
|
74
|
-
|
|
75
|
-
**请裁决**:是
|
|
76
|
-
- (a) 直接改写该报告为"如实版"(丢失原始误报的痕迹),还是
|
|
77
|
-
- (b) **保留原报告不动 + 追加一份勘误节**("原始报告哪些项高报、实测如何")——**倾向 (b)**,因为能保留"为什么会误报"的教训?
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## 三、我方的初步判断(供对方否决)
|
|
82
|
-
|
|
83
|
-
| 项 | 我的判断 | 置信度 |
|
|
84
|
-
| --- | --- | --- |
|
|
85
|
-
| Q1 修法 | **A1-b** | 中(未验证 cordis 是否提供启动期配置,故需 GPT 确认 A1-c 是否更正统) |
|
|
86
|
-
| Q2 顺序 | **(a) 先补 graph 端到端套件** | 高(否则无法证明修好了) |
|
|
87
|
-
| Q3 范围 | **补 P3-3 + P2-5 必修项;P2-3/P2-4/P3-2 看本批余量** | 中 |
|
|
88
|
-
| Q4 接线 | **(b) 另立一步**——但必须让用户明确知道"不接线 = 看不到看板" | 高(改 profile 风险高,且用户硬规矩要求先备份+同意) |
|
|
89
|
-
| Q5 锚点 | **算未兑现,建议补**(否则 §2 收益条款是空头承诺) | 中高 |
|
|
90
|
-
| Q6 报告 | **(b) 追加勘误节** | 高 |
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## 四、我方**不做**的事(边界声明)
|
|
95
|
-
|
|
96
|
-
- 不改用户 profile(Q4 未裁决前)。
|
|
97
|
-
- 不 commit/push/publish(代码全留 pre 线)。
|
|
98
|
-
- 不重启 dsh web 宿主(用户自行重启)。
|
|
99
|
-
- 不为了"让测试变绿"而放宽断言——尤其不能把三处工具数硬锁从 `!== 14` 直接改成"随便多少"。
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## 五、对方回复的期望格式
|
|
104
|
-
|
|
105
|
-
请逐条回:
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
Q1: 采纳 <A1-a|A1-b|A1-c|其他:____>,理由 <一句话>
|
|
109
|
-
Q2: <a|b>,理由 <一句话>
|
|
110
|
-
Q3: 本批范围 = <列出项号>,其余延后
|
|
111
|
-
Q4: <a|b>,理由 <一句话>
|
|
112
|
-
Q5: <补写锚点|延期>,理由 <一句话>
|
|
113
|
-
Q6: <a|b>
|
|
114
|
-
补充意见: <如果有我漏掉的方向性问题>
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## 六、附:材料索引
|
|
120
|
-
|
|
121
|
-
| 文件 | 内容 |
|
|
122
|
-
| --- | --- |
|
|
123
|
-
| `docs/internal/AUDIT-WB-GRAPH-FULL-20260916.md` | 任务一全量粗检报告(4 致命 + 5 新缺陷 + 规划三态对账 + 修复方案 + 证据清单) |
|
|
124
|
-
| `docs/internal/REVIEW-WB-GRAPH-SELF.md` | 16 日凌晨自审(3 致命 + 6 高/中危,**其中 BUG-4 已被本次推翻**) |
|
|
125
|
-
| `docs/internal/GPT-REVIEW-PROMPT.md` | 找 bug 的任务书(与本文互补:那份找 bug,这份定方向) |
|
|
126
|
-
| `docs/internal/WB-GRAPH-INTEGRATION-PLAN.md` §5 | 改动清单原表(P0-P1/P2/P3 全部条目) |
|
|
127
|
-
| `docs/internal/WB-FORMAT-CONVENTION.md` | §2 锚点契约 / §3 索引派生 / §4 写入门 / §8 验收清单 |
|
|
128
|
-
| `docs/internal/WB-GRAPH-DECISIONS-20260914.md` §E | 16 日凌晨拍板留痕(A1-A8/B4/B7/P1 范围) |
|
|
129
|
-
| `docs/internal/REPORT-WB-GRAPH-NIGHTLY.md` | 交付报告(**含高报项,Q6 待裁决**) |
|
|
130
|
-
| `artifacts/_audit-p23-probe.mjs` | 可复跑:BUG-4 推翻 / tag 正则 / 返回契约 / boardMode 解析 |
|
|
131
|
-
| `artifacts/_audit-id-repro.mjs` | 可复跑:BUG-10 两条路径 id 对撞 |
|
|
132
|
-
| `artifacts/_audit-apply-await.mjs` | 可复跑:apply 函数体花括号配对 + await 深度分布 |
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
站长你好,反馈一个 `openai-responses` 协议下的冲突问题。
|
|
2
|
-
|
|
3
|
-
**现象**:用 `api: openai-responses` 走 `https://api.dshapi.icu/v1` + `deepseek-v4.1-flash`,客户端(DSH)的会话会永久损坏——所有模型开始报 `400 Duplicate 'call_id'`,之后该会话再也不能用,只能手工清历史。
|
|
4
|
-
|
|
5
|
-
**原因**:responses 协议里工具调用的 id 字段和你返回的 `tool_calls[].id` 用的是一样的 `call_xx_xxxx` 命名规则,两边算出来的 id 会撞车。DSH 在 responses 这条路径上拿不到独立的 id,只能用自己生成的值兜底,同一轮里并发多个工具调用时就重复了,写进历史后每次请求都被上游拒绝。
|
|
6
|
-
|
|
7
|
-
**对比证据**:同一份客户端配置下,其他中转(sub-vankit、vankit-glm 等)都没有这个问题,只有走你们 responses 端点时会撞。
|
|
8
|
-
|
|
9
|
-
**想请你确认/解决**:
|
|
10
|
-
1. 你们是否支持标准的 `chat/completions` 协议?我们这边实测 `https://api.dshapi.icu/v1/chat/completions` 是正常返回的,如果可以,我们改用标准协议就能绕过。
|
|
11
|
-
2. 如果必须用 responses,能否让返回里的工具调用 id 与客户端请求中的 id 明确区分开(比如加独立前缀),避免撞车?
|
|
12
|
-
|
|
13
|
-
麻烦看一下,谢谢!
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# 补充确认:同轮内重复 tool_call_id(相邻双写形态)
|
|
2
|
-
|
|
3
|
-
## EN
|
|
4
|
-
|
|
5
|
-
**Environment**: Windows 11 / Node 24 / dsh `0.1.5-rc.1`, strict OpenAI-compatible relay (Console Go, unique-id check).
|
|
6
|
-
|
|
7
|
-
**Symptom**: the session becomes permanently unusable — every model fails with:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
400 invalid_request_error: Duplicate 'call_id': call_01_IBQv9P78wRhPI72L2XVh2573_call_01_IBQv9P78wRhPI72L2XVh257.
|
|
11
|
-
400 Duplicate value for 'tool_call_id' of call_01_IBQv9P78wRhPI72L2XVh2573|call_01_IBQv9P78wRhPI72L2XVh2573 in message[3]
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
The UI also fails to render: `[session-controller] event feed subscriber failed: conversation Context 20:trajectory-tool-call <id>|<id> received more than one start Match`.
|
|
15
|
-
|
|
16
|
-
**Evidence (3 sessions on disk, one night)**: the model emitted the same `tool_call_id` twice **within one step**; DSH persisted both verbatim:
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
981 tool/call call_01_UKmzZe2AlTekpCR0whQV6449|call_01...
|
|
20
|
-
983 tool/call call_01_UKmzZe2AlTekpCR0whQV6449|call_01... <-- same id, 2 events apart
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Two of the three cases followed an aborted turn; **the third had no abort** — so this is not abort-specific.
|
|
24
|
-
|
|
25
|
-
**New finding — two shapes, only one is acute**: scanning 164 local session files, 30 contain duplicated ids:
|
|
26
|
-
|
|
27
|
-
| shape | gap | sessions | effect |
|
|
28
|
-
|---|---|---|---|
|
|
29
|
-
| adjacent double-write (same step) | 2 events | 3 | session permanently unusable |
|
|
30
|
-
| cross-turn reuse (different turn) | tens of thousands of events | 27 | tolerated today, one strict-provider switch from the same fate |
|
|
31
|
-
|
|
32
|
-
**New finding — a diagnostic signature that identifies the culprit provider**: DSH stores each tool call id as `<recorded>|<provider-mapped>`. Across all local sessions the two halves are **identical** (self-reflection: the provider echoes DSH's id back instead of returning its own) for exactly one provider:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
provider self-reflected normal-mapped no-separator
|
|
36
|
-
dshapi 11 0 0 <-- 100% self-reflected
|
|
37
|
-
sub-vankit 0 1431 ...
|
|
38
|
-
vankit-glm 0 821 ...
|
|
39
|
-
opencode-go2 0 1783 ...
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
When the two halves are equal, concurrent tool calls in one step collide and produce the byte-identical `<id>|<id>` duplicates. So a fast triage question is: **"is your provider self-reflecting the ids?"** — check `data.callId` in any `tool/call` event; `X|X` = the provider is echoing ids instead of minting them.
|
|
43
|
-
|
|
44
|
-
**Repair (works, host restart required)**: drop the second `tool/call`+`tool/result` pair, keep all other events. Constraint: `session.v3.jsonl.zstd` is append-style multi-frame zstd with **one event per frame**, and the first frame must be *exactly* one header line — recompressing into a single frame bricks boot (`corrupt Zstandard session log: first frame is not exactly one header line`).
|
|
45
|
-
|
|
46
|
-
**Suggested fix**: enforce session-wide unique `toolCallId` at history append (rewrite the later occurrence + remap its `tool` message), and make the assembler isolate rather than fatal-throw on the duplicate `start Match` (same ask as the #5692 family).
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 中文
|
|
51
|
-
|
|
52
|
-
**环境**:Windows 11 / Node 24 / dsh `0.1.5-rc.1`,严格校验唯一 id 的 OpenAI 兼容中转(Console Go)。
|
|
53
|
-
|
|
54
|
-
**现象**:会话永久不可用——所有模型都报上面那两条 400;界面同时卡加载(`received more than one start Match`)。
|
|
55
|
-
|
|
56
|
-
**证据(一夜 3 个会话,磁盘日志)**:模型在**同一个 step 内**输出了两次相同的 `tool_call_id`,DSH 原样持久化两条(seq 981 → 983,相隔 2 条事件)。三次中两次在中断回合之后,**第三次没有任何中断**——并非中断专属。
|
|
57
|
-
|
|
58
|
-
**新发现一——两种形态,只有一种致命**:扫本地 164 个会话,30 个含重复 id——**相邻双写**(差 2 条,3 个会话,会话永久不可用)vs **跨 turn 复用**(相差数万条,27 个会话,当前被容忍但切换严格 provider 即同样失效)。
|
|
59
|
-
|
|
60
|
-
**新发现二——一个能直接指认元凶的鉴别特征**:DSH 把每次工具调用的 id 存成 `<记录的>|<provider 映射的>`。全部本地会话统计下来,只有一家 provider 的**两半完全相同**(自反映射:provider 不回吐自己的 id,而是把 DSH 给的 id 原样送回):
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
provider 自反映射 正常映射 无分隔
|
|
64
|
-
dshapi 11 0 0 ← 100% 自反映射
|
|
65
|
-
sub-vankit 0 1431 ...
|
|
66
|
-
vankit-glm 0 821 ...
|
|
67
|
-
opencode-go2 0 1783 ...
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
两半相同时,同一 step 内的并发工具调用就会撞在一起,产生字节相同的 `<id>|<id>`。所以快速判断法:**看你 provider 是不是在自反映射**——打开任意 `tool/call` 事件的 `data.callId`,若是 `X|X` 形态,即 provider 没有自己生成 id 而是回吐了请求里的 id。
|
|
71
|
-
|
|
72
|
-
**修复方式(有效,需重启宿主)**:删除第二次出现的 `tool/call`+`tool/result` 对,其余保留。约束:会话日志是追加式多帧 zstd、**一帧一事件**,首帧必须恰好是 session 头一行——压成单帧会导致启动失败。
|
|
73
|
-
|
|
74
|
-
**建议修复**:历史 append 处强制会话内 `toolCallId` 唯一(改写后出现的那次 + 重映射其 `tool` 消息);assembler 在重复 `start Match` 时隔离该块而非致命抛出(与 #5692 家族一致)。
|