better-dsh 0.2.2-c → 0.2.3-b

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.
Files changed (27) hide show
  1. package/docs/50_test-reports/2026-09-06-write/345/267/245/345/205/267sandbox/345/215/207/347/272/247/351/200/217/344/274/240bug/345/244/215/345/217/221/345/217/212/346/214/202/350/265/267-/344/272/213/344/273/266/346/212/245/345/221/212.md +133 -0
  2. package/docs/50_test-reports/2026-09-08-hashline-edit-E_RANGE_UNVERIFIED/350/267/250/350/275/256/344/274/232/350/257/235/351/224/256/345/244/261/346/225/210-/350/257/212/346/226/255/346/212/245/345/221/212.md +226 -0
  3. package/docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-local-test-report.md +44 -0
  4. package/docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-report.md +110 -0
  5. package/docs/50_test-reports/v0.2.3b-hashline-content-locator/345/256/236/346/265/213/346/212/245/345/221/212.md +73 -0
  6. package/docs/60_exploration-and-research/01-cordis-runtime/cordis-engineering-feasibility.md +155 -0
  7. package/docs/60_exploration-and-research/01-cordis-runtime/cordis-research.md +385 -152
  8. package/docs/60_exploration-and-research/01-cordis-runtime/js-ts-language-fundamentals.md +628 -0
  9. package/docs/60_exploration-and-research/02-dsh/dash-research.md +592 -0
  10. package/docs/60_exploration-and-research/02-dsh/dsh-web-profile-package-map.md +186 -0
  11. package/docs/60_exploration-and-research/02-dsh/dsh-web-ui-slot-system-research.md +310 -0
  12. package/docs/60_exploration-and-research/02-dsh/dsh-webui-backend-data-inventory.md +633 -0
  13. package/docs/60_exploration-and-research/02-dsh/dsh-webui-strip-boundary-research.md +300 -0
  14. package/docs/60_exploration-and-research/02-dsh/dsh-webui-wire-appendix.md +3729 -0
  15. package/docs/60_exploration-and-research/02-dsh/web-frontend-composability-research.md +191 -0
  16. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/capture-live-turn.json +1 -0
  17. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/observed-endpoints.json +224 -0
  18. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/remote-inventory.json +110954 -0
  19. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/served-index-sample.html +47 -0
  20. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/session-events.json +9411 -0
  21. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/ws-frame-examples.json +20 -0
  22. package/docs/superd/00-blueprint.md +331 -0
  23. package/docs/superd/01-component-boundaries.md +261 -0
  24. package/docs/superd/02-dsh-embed-modes.md +151 -0
  25. package/docs/superd/image.png +0 -0
  26. package/lib/index.js +1214 -1293
  27. package/package.json +1 -1
@@ -41,3 +41,136 @@ agent 推理链(UI 可见):此前一次 bash 升级到 workspace-write 被
41
41
  以及挂起问题——需区分"审批提示未渲染"与"promise 未 settle",二者修法不同。
42
42
  3. 回归范围:至少覆盖 write / edit / undo_last_edit 三个文件工具 × workspace-write 与
43
43
  read-only 两种基线模式下的升级路径。
44
+
45
+ ## 同日复测(本地 Web GUI):write 升级链路实测通过
46
+
47
+ > 环境差异:prod 3080 / GLM → 本地 Web GUI(127.0.0.1:4999)/ deepseek-v4-flash;
48
+ > file policy = workspace-write(workspace:`/home/u1/workspaces/temp`),approval policy = ask。
49
+ > 测试目标(workspace 之外):`/home/u1/workspaces/base/.scratch/dsh_ws_test.txt`
50
+
51
+ ### 实测记录
52
+
53
+ 1. **基线越界 write → 立即被拒,无审批卡**:
54
+ `[sandbox: file access denied under workspace-write mode]`
55
+ 且拒绝消息自带第二行 affordance 提示(本轮行为的关键信号源):
56
+ `[sandbox: escalation available — retry this exact operation once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]`
57
+ 2. **单次升级重试**:`sandbox_permissions: "danger-full-access"` + justification → 返回
58
+ `Created`(审批通过),未再出现 denial;磁盘校验 `-rw------- 1 u1 u1 26`,内容
59
+ `test write from dsh agent`。
60
+ 3. 全程**无挂起、无字段丢弃、无静默失败**。
61
+
62
+ **结论**:本地构建上 write 升级链路(字段透传 → 审批渲染 → promise settle)实测通过,与 prod 3080
63
+ 报告的「字段丢弃 + 挂起」复发症状不一致 → 缺陷疑为环境/构建相关(或已被修复)。建议按上文
64
+ 「建议 3」在 prod 3080 上回归 write / edit / undo_last_edit × workspace-write 与 read-only 基线。
65
+
66
+ ## 附 A:escalation 提示机制的优化(reminder 文案)
67
+
68
+ 同一机制在 system prompt 中至少出现三处,语义同一但角色不同:
69
+ ① 工具 schema 参数声明(affordance / API 表面,只声明字段存在);
70
+ ② policy 规则段(procedure:何时、怎么用 + 大量禁令);
71
+ ③ runtime-context 快照补充句(情境重启,位于每次 turn 顶部、工具调用前最后读到的位置)。
72
+
73
+ 本轮讨论聚焦③的措辞。现文:
74
+
75
+ > Restricted operations may be retried once with sandbox_permissions for single-call escalation,
76
+ > pending user approval.
77
+
78
+ 对字面论 / 风险厌恶型模型,存在两种可被误读的推理路径:
79
+
80
+ (a) **配额式误读**:「once」被理解为一次性越权许可——只能带 escalation 请求一次;若首次升级被
81
+ user deny,模型可能推断整个机制已耗尽、连后续命令也不再尝试升级。但 runtime **无状态**:不记录某
82
+ 命令是否已 escalate / 被 deny,系统不存在这类配额。
83
+ (b) **ask-first 误读**:「pending user approval」被读成先征得批准再 retry,与 policy 的
84
+ 「don\'t detour through chat — the retry itself raises the approval prompt」相悖。
85
+
86
+ 「once」的字面语义与真实策略(单次 retry)并不冲突;歧义在于**未声明机制的 per-call / 无状态属性**,
87
+ 而该属性正是阻止 (a) 误读的关键事实。因此措辞应从「配额式」改为「机制式」,显式声明:per-call、
88
+ 无配额记录、审批由 retry 自动触发、单次 deny 只作用于该 call。
89
+
90
+ **定稿(同日终版,替换③;上文「主推 / 短变体」作废)**:
91
+
92
+ > A sandbox-deniable/denied call may be escalated with `sandbox_permissions="danger-full-access"` and a
93
+ > one-line `justification`; the runtime will prompt for user approval. Escalation and its
94
+ > approval/denial are per-call.
95
+
96
+ 双词并列**中立覆盖两条路径**:`deniable`——读到本段、且按 policy 可演绎判定为越界的调用(workspace +
97
+ temporary 之外)直接升级,不做 doomed 往返;`denied`——未注意本段、已被拒的调用同样可升级。全文无
98
+ retry / once / 配额措辞。安全边界是**双重硬 guard**:sandbox(policy 判定)+ user approval(审批卡);
99
+ 预升级至多多弹一张可拒审批卡,无实害,「等审批」的心理摩擦天然抑制滥用。`danger-full-access` 经源码
100
+ 核对(`escalation.ts` `WIDER_MODES`:workspace-write → [danger-full-access],且本句仅在该模式下渲染),
101
+ 参数名与 justification 单句配对均与 schema 一致。
102
+
103
+ **具体 example(agent 视角最小推演)**:
104
+
105
+ ```
106
+ #1 write(workspace 外路径) → denied(无审批,返回 marker + affordance 行)
107
+ #2 write(同路径, sandbox_permissions="danger-full-access", justification="…") → 弹审批卡
108
+ user 点 deny
109
+ → 语义:该 call 就此结束(stop and explain),≠ 「escalation 机制已耗尽」
110
+ #3 稍后另一条命令被拒 → 仍可走同一升级路径(per-call,runtime 无配额记录)
111
+ ✗ 错误推理:#2 被 deny ⇒ 我不能再发起任何 escalation
112
+ ```
113
+
114
+ **配套建议**:
115
+
116
+ - ②policy 段的关键句(rejected = final for that command,但不禁止后续命令升级)与③措辞需锁死一致,
117
+ 避免三处漂移;
118
+ - 最强一层是**环境内信号**:denial 消息自带 affordance 行(本轮实测可见,现构建已带)。它零 token、
119
+ 位置恰好、不可能被 attention 漏掉;应作为固定格式保留,并让提示中的 mode / 参数名与消息完全一致。
120
+
121
+ ## 附 B:保留并补充该 reminder 的必要性(语态 / 效价问题)
122
+
123
+ 系统 prompt 原本已含等价语义(②policy 段),但该段 **permission 密度低、被高强度禁令包围**
124
+ (Do not detour… / Never escalate speculatively / A rejected escalation is final / no exception /
125
+ a denial is final)。LLM 对禁令性词语的权重显著高于许可性陈述;风险厌恶型模型读后的净效价收敛为
126
+ 「escalation 是高风险的非常规动作,能避则避」——这为观测到的「静默失败」(denial 后直接放弃、从不
127
+ 尝试升级)提供了行为学解释:**这不是注意力丢失,而是语态 / 效价问题**。
128
+
129
+ ③是全文唯一「零否定、纯许可」的 escalation 表述,且位于 turn 顶部快照(决策前最后读到)→ 命题上
130
+ 与②重复,但功能上不冗余:它承载 **grant(放行)**,②承载 **procedure(程序 + 禁令)**。本轮复测中
131
+ agent 在 denial 后正确、立即升级,除错误消息 affordance 外,③的纯许可语态是「敢试」的直接依据。
132
+
133
+ 结论:③应保留并按附 A 修正措辞;不要在静态 prompt 中继续增加第四处副本(边际收益到顶),后续投资
134
+ 应放在 denial 消息的 affordance 质量(直接给出该传的 mode)。
135
+
136
+ ## 附 C(新发现 · 待修):rules 段「escalating up front is fine only when this session already denied the same access」自相矛盾
137
+
138
+ > 追加于定稿同日;定位为文本漂移,修复另行排期,本轮未改动任何源码。
139
+
140
+ **归属结论(同日追查)**:该子句位于**上游** DSH v0.1.2-alpha.5 源码——本 checkout 即
141
+ github.com/deepseek-ai/deepseek-harness 的浅克隆(HEAD `db6bdc3576`,唯一 remote 为上游
142
+ origin);`packages/shell/tool-bash/src/index.ts:89` 与 `packages/shell/tool-pwsh/src/index.ts:140`
143
+ 均经 `git ls-files` 确认是上游跟踪文件。blame 止于 `47f943859b`(2026-08-13,上游 PR #2519
144
+ merge)系浅克隆边界,真实引入 commit 更早、被截断。对照:刚定稿的快照句(dashr:escalation-
145
+ guidance,`packages/better-dsh/better-dsh/src/index.ts`)属 Better Dash(repo:pgmi-builds/
146
+ better-dsh),在上游 tree 内为 untracked(`??`)叠层。**修复含义**:改矛盾句 = 改上游跟踪
147
+ 文件,不能直接 commit 进本 checkout;两条路——上游 PR,或在 better-dsh 的 prompt 装配层覆盖
148
+ 订正 bash/pwsh 工具描述(与 dashr:escalation-guidance 同款注入方式)。
149
+
150
+ **自相矛盾分析**:按字面语义,「escalating up front」(未尝试即升级)与「already denied the
151
+ same access」(已尝试且被拒,同一 access)互斥——后者成立则前者必然不成立,条件永远无法满足。
152
+ 属两条规则被硬拼成一句的编辑事故。**推断的原意**:同一 access 已被本 session 拒绝过一次之后,
153
+ 再次需要时不必重发 doomed 调用、可直接升级(豁免重复往返)——但这层豁免语义句子本身没写出来。
154
+
155
+ **事实核对(源码)**:
156
+
157
+ - 该子句仅存在于两处模型可见的工具描述:`packages/shell/tool-bash/src/index.ts:89`、
158
+ `packages/shell/tool-pwsh/src/index.ts:140`;并随描述文本进入 35 个
159
+ `snapshots/**/tool-schemas*.expected.json` 快照(随源重生成,不需手改)。
160
+ - 同策略的文档版本(tool-bash / tool-pwsh 的 README.md 与 README.zh.md)**均无此子句**
161
+ (0 命中)——同一策略在工具描述与文档中本就是两个版本(次级漂移)。
162
+ - runtime 无 prior-denial 状态:`approveEscalation`(`packages/sandbox/sandbox/src/escalation.ts`)
163
+ 只有三道闸——参数配对、严格更宽(`WIDER_MODES`)、审批通道。文档所称
164
+ 「a request with no real prior denial … fails closed」在代码中并未实现为状态检查;
165
+ 该子句纯属 prompt 层规范。
166
+
167
+ **与本轮定稿的关系**:附 A 定稿(快照句)规定 deniable(policy 可演绎判定越界)调用可直接升级;
168
+ 本子句规定 upfront 升级仅在 same-access 已 denied 后才允许——同一动作两个触发定义,且其中之一
169
+ 自相矛盾。rules 段禁令权重高(见附 B 效价分析),模型大概率取 deny-first 读法,与快照句的
170
+ deniable 路径持续张力。
171
+
172
+ **修复方向(未实施,待排期)**:把豁免语义显式化,例如「once this session has already denied
173
+ the same access, you may escalate again without re-firing the doomed call」;或与 deniable 原则
174
+ 统一为「policy 可演绎判定将被拒的调用可直接升级」。需同步:tool-bash / tool-pwsh 工具描述 ×2、
175
+ README ×2(en/zh 一并对齐)、35 个快照重生成;并与 deniable/denied 的 deny-first vs 预判升级
176
+ 产品决策对齐后再动。
@@ -0,0 +1,226 @@
1
+ # 诊断报告:hashline edit `E_RANGE_UNVERIFIED` 跨轮会话键失效
2
+
3
+ > 日期:2026-09-08 · 环境:DSH v0.1.2-alpha 线(Web GUI),file policy = workspace-write
4
+ > (workspace:`/home/u1/workspaces/dashr`),better-dsh hashline edit 桥(vendored
5
+ > `dashr/src/url-schema/vendored/hashline/`,dsh-better-edit 工具集)。
6
+ > 性质:**行为诊断**(非实测计划);一次真实触发 + 源码级与账本级取证。
7
+ > 前置关联:`2026-09-06-write工具sandbox升级透传bug复发及挂起-事件报告.md`(同桥另一缺陷线)。
8
+
9
+ ---
10
+
11
+ ## 0. 一句话结论
12
+
13
+ `E_RANGE_UNVERIFIED` 拒绝**不是校验代码的 bug**:write 后 auto-read 确实记了账,但 hashline
14
+ 的 served 账本按 **(session_id, path)** 键存储,而本部署**每个对话轮(continuation round)更换
15
+ harness session id**——上一轮 write-hook 记的账,下一轮 edit 用自己的新键去查,必然查空。锚点
16
+ 与磁盘内容当时完全匹配(echo 可证),拒绝是**出处(provenance)账本问题,不是 staleness**。
17
+ fail-closed 与批量原子性按设计工作,零数据损伤;代价是**跨轮编辑必须冗余 read 一次**。
18
+
19
+ ---
20
+
21
+ ## 1. 现象与错误解剖
22
+
23
+ 原始报错(13-tuple 批量 edit 的第 0 个 tuple):
24
+
25
+ ```
26
+ Error: [E_BATCH_ABORT] edits[0] (…/docs/superd/01-component-boundaries.md) failed:
27
+ [E_RANGE_UNVERIFIED] cannot verify range against served state in …:
28
+ remove_from "Utw" has no served position; remove_to "Utw" has no served position.
29
+ Current range:
30
+ Utw│# Super D 组件边界与 UI 组合层(v0.1)
31
+ Retry with these anchors (no read needed). … The whole batch was rejected and
32
+ NOTHING was written — no file changed and earlier items in the batch were NOT applied.
33
+ ```
34
+
35
+ 三层套叠,每层各自按契约工作:
36
+
37
+ | 错误码 | 含义 | 触发条件 | 本次是否命中 |
38
+ |---|---|---|---|
39
+ | `E_RANGE_UNVERIFIED` | 出处校验失败:锚点 hash 在**本会话 served 账本**查无位置 | 账本没记过"你看过这行" | ✅(本案) |
40
+ | `E_RANGE_STALE` | 内容漂移:served 位置上的 hash 与当前文件不一致 | 文件在 serve 之后被改过 | ❌ |
41
+ | `E_RANGE_UNSERVED` | 区间中洞:起止锚在账,中间行从未 served | 部分服务(截断读后编辑跨页) | ❌ |
42
+ | `E_BATCH_ABORT` | 批量原子中止:任一 tuple 失败 ⇒ 整批不写、先前 tuple 也不应用 | 防半套状态 | ✅(外层包装) |
43
+
44
+ 注意 echo 行 `Utw│# Super D 组件边界与 UI 组合层(v0.1)` 与磁盘逐字节一致——**锚点本身完全
45
+ 有效**,拒绝纯粹因为账本查无出处。
46
+
47
+ ---
48
+
49
+ ## 2. 事发回放(时间线)
50
+
51
+ | 时刻 | 轮 | 动作 | 结果 |
52
+ |---|---|---|---|
53
+ | 轮 B | 蓝图 + 边界文档轮 | `write` 创建 `01-component-boundaries.md`(244 行) | write-hook 触发:结果尾部带 `--- Auto-read (hashline anchors) ---`,全文件锚点(`Utw…Pb8`);`readAndServe → recordServed` 记账,键 = 轮 B 的 session id |
54
+ | 轮 B→C | 轮边界 | harness 换 session id | (无感知) |
55
+ | 轮 C | 透传/认证轮 | 拿 auto-read 锚点提交 13-tuple edit | `loadServed(轮C键, path)` = 空 → `E_RANGE_UNVERIFIED` → 整批拒绝,**零写入** |
56
+ | 轮 C | 同轮 | `read` 全文(重新记账到轮 C 键)→ 原样重交 | 13/13 全部成功 |
57
+
58
+ ---
59
+
60
+ ## 3. 取证
61
+
62
+ ### 3.1 代码链(vendored hashline,全部一手核对)
63
+
64
+ | 环节 | 文件 | 事实 |
65
+ |---|---|---|
66
+ | 拒绝点 | `hashline/anchor-pipeline.js:462` `verifyServedRange()` | 先 `servedPositionsOf(served, hash)` 查账:查无 → `E_RANGE_UNVERIFIED`;查到但内容不匹配 → `E_RANGE_STALE`;区间中洞 → `E_RANGE_UNSERVED` |
67
+ | 批量原子性 | `edit-engine.js:270` | 任一 item 失败 → `[E_BATCH_ABORT]` + 当前区间 echo,整批不写 |
68
+ | write 记账 | `write-hook.js`(`tools/post-execute` 监听) | write 成功后调 `readAndServe(io, path, cwd, { sessionKey: execSessionKey(exec) })`——**渲染锚点与记账是同一次调用**,preview 出现 = 记账执行过;catch 兜底只影响回显不影响 write 本体 |
69
+ | 账本读写 | `read-and-serve.js` → `session-view.js` `recordServed/loadServed` | served 按 **(session_id, path)** 唯一键 upsert |
70
+ | 键推导 | `session-view.js:47-58` | `sessionKeyFor(exec.agent?.session.id)`;缺 id 时落到进程级 `randomUUID()` 兜底键 |
71
+ | 存储位置 | `paths.js` `configDir(cwd)` | `<workspace>/.dsh_better_edit/hash-store.sqlite`;workspace = `exec.agent.session.header.cwd`(`withWorkspace` 传播) |
72
+
73
+ ### 3.2 账本验尸(sqlite 实查)
74
+
75
+ 库:`/home/u1/workspaces/dashr/.dsh_better_edit/hash-store.sqlite`(表:snapshots / meta / undo /
76
+ served;served 666 行、579 快照)。
77
+
78
+ 本对话相关行(节选,按时间):
79
+
80
+ ```
81
+ 09-08 01:18 session-cb07ad10… (轮 A:dash-research 深化轮)
82
+ 09-08 02:13 session-a1b3097d… 01-component-boundaries.md ["Utw","AuN",…] ← v0.1 布局
83
+ 09-08 02:15 session-a1b3097d… 00-blueprint.md ["aD5",…]
84
+ 09-08 03:20 session-a61bbf92… 00-blueprint.md ["C7S",…](v0.3 期)
85
+ 09-08 03:41 session-328368a6… 01-component-boundaries.md ["pKk",…] ← v0.3 后布局
86
+ 09-08 03:53 session-328368a6… 00-blueprint.md ["rJz",…]
87
+ ```
88
+
89
+ 关键观察:
90
+
91
+ 1. **每轮一个键**:served 表共 50+ 个 distinct session 键(含大量无 `session-` 前缀的裸 UUID——
92
+ `sessionKeyFor(undefined)` 的进程兜底键,来自 preview/test 形态)。本对话各轮 `memory_flush`
93
+ 回显的 session id 与账本键一一对应(轮 A=`cb07ad10…`、轮 B=`7104efad…`),**轮 B 的键在
94
+ 本库无任何行**——它的 read/edit/write-hook 记账落在了别处(store 跟随 exec cwd)或同样因
95
+ 轮界不可见;轮 C 的编辑因此查空。
96
+ 2. **(session, path) upsert 语义**:同键同路径后写覆盖前写,跨键各留一行——这正是"跨轮每换
97
+ 一键就重读一次"摩擦的账面痕迹(blueprint 在 4+ 个键下各有一行)。
98
+ 3. **snapshots 表全局按 path**(不分会话)——与 served 的会话隔离形成对照:文件指纹证据本来
99
+ 就有全局权威,只有"谁看过"是会话私有的。
100
+
101
+ ### 3.3 判定时刻的锚点有效性
102
+
103
+ 拒绝时的 echo 显示 `Utw` 行与磁盘一致;且调用方必须先在**当前文件哈希表**里定位出锚点区间才能
104
+ 构造该 echo(`verifyServedRange` 收到 `fileHashes` 与定位后的 `startLine/endLine`)。即:**锚点
105
+ 与磁盘逐字节匹配的条件下仍然被拒**——拒绝条件是纯账本出处,非内容漂移。这是提出修复方案 1
106
+ (content-match 快道)的直接依据。
107
+
108
+ ---
109
+
110
+ ## 4. 判定
111
+
112
+ **没有坏的部分**(逐项核对通过):
113
+
114
+ - 校验逻辑三分支语义正确、先账本后内容、顺序合理;
115
+ - write-hook 记账链无缺陷(渲染与记账同调用,preview 出现即记账执行);
116
+ - fail-closed:查不到出处一律拒绝,绝不带疑写入;
117
+ - 批量原子性:`E_BATCH_ABORT` 整批回滚,先前的 tuple 不落盘(本案 13 tuple 零写入);
118
+ - 恢复路径干净:一次 `read` 重记帐后原样重交即全绿。
119
+
120
+ **缺口**(设计假设与部署现实不符):
121
+
122
+ - 账本假设"一个逻辑对话 = 一个稳定 session id";本部署每个 continuation round 换新 id(且
123
+ store 目录跟随 exec cwd),**跨轮 served 必然不可见** ⇒ 每个轮界对每个待编辑文件强制一次
124
+ 冗余 read——纯摩擦、无风险、可完全消除。
125
+ - 报错文案 `"Retry with these anchors (no read needed)"` 对多 tuple 批是误导:echo 只把
126
+ `edits[0]` 的区间记为 served,原样重交会在 `edits[1]` 上再失败、循环往复;正确指引是
127
+ "read 一次后整批重交"。
128
+
129
+ ---
130
+
131
+ ## 5. 修复建议(better-dsh,按性价比排序)
132
+
133
+ 1. **content-match 快道(推荐)**:`verifyServedRange` 已持有当前 `fileHashes` 与定位区间——
134
+ 当锚点 hash 在当前文件的定位区间上**逐行全匹配**时直接接受。byte-exact 保障不损(全匹配
135
+ ⇔ 自锚点生成以来该区间未变),served 账本从"门禁"降级为 echo/undo 的辅助。整体消灭此类
136
+ 摩擦,且不依赖 harness 侧任何改动。
137
+ 2. **served 键去 session 化**:per-workspace 一份;或当前键为空时采纳该 path 的最近 serves
138
+ (`snapshots` 已是全局按 path 的先例)。改动小,但弱化"多会话并行同一文件"的隔离语义,
139
+ 需评估并发场景。
140
+ 3. **改键源**:若 harness exec 暴露 conversation/root id(resume/continuation 的稳定根),
141
+ 以其代替 `session.id` 做键——语义最正,依赖上游字段存在。
142
+ 4. **廉价修(文案)**:多 tuple 批失败时指引改为"对本文件 read 一次后整批重交";顺带在
143
+ `E_RANGE_UNVERIFIED` 文案中点明"write auto-read 的锚点跨轮不可复用"这一部署事实。
144
+
145
+ 建议 1 与 2/3 不互斥:1 消摩擦,2/3 修语义。落地走 openspec change 流程(含单测:write → 模拟
146
+ 换 sessionKey → edit 应成功/回退行为明确)。
147
+
148
+ ---
149
+
150
+ ## 6. 复现与回归要点
151
+
152
+ - 触发条件:轮 N `write`(或 read)文件 → 轮 N+1 直接 edit 同文件 → 必现
153
+ `E_RANGE_UNVERIFIED`(首个 tuple 即拒)。
154
+ - 快速复现(不依赖轮界):同文件两次 edit 之间,用不同 `sessionKey` 调 `loadServed` 即等价
155
+ (单测可直接构造)。
156
+ - 回归断言:修复后 write/read 的锚点在**任意后续轮**直接可用;`E_RANGE_STALE`(真漂移)与
157
+ `E_RANGE_UNSERVED`(区间中洞)行为不变;批量原子性不变。
158
+
159
+ ## 来源
160
+
161
+ - 一手源码:`dashr/src/url-schema/vendored/hashline/{write-hook,read-and-serve,session-view,
162
+ paths}.js`、`hashline/anchor-pipeline.js`(verifyServedRange)、`edit-engine.js`(批量中止)
163
+ - 一手账本:`/home/u1/workspaces/dashr/.dsh_better_edit/hash-store.sqlite`(served/snapshots/
164
+ undo 表实查,2026-09-08)
165
+ - 触发实录:本 session 轮 B→轮 C(`docs/superd/01-component-boundaries.md`,13-tuple 批,
166
+ 拒绝后 read 重交 13/13 成功)
167
+
168
+ ---
169
+
170
+
171
+ ## 7. 勘误与设计裁决(2026-09-08 第二轮复核,user 主持)
172
+
173
+ > 本节修正上文 §0/§2/§4 的关键归因错误,并记录用户裁决的修复方向。原文保留作诊断过程记录。
174
+
175
+ ### 7.1 勘误:"每轮换 session id" 不成立,真实机制是会话树 fork
176
+
177
+ | 上文断言 | 复核结论 | 一手证据 |
178
+ |---|---|---|
179
+ | "本部署每个 continuation round 更换 harness session id" | **不成立**。账本键 = `exec.agent?.session.id` 原样(`session-view.js:47-58`,无加工),与 `~/.dsh/sessions/<workspace>/session-<uuid>/` 目录及 `session.jsonl.zstd` header.id **一一对应**;同 id 跨多小时/多轮复用(328368a6:03:41+03:53 两笔;2f3a8234:04:27→11:41 跨 7h,本报告即该会话所写) | dashr 桶 6 个 session 目录与账本键全对上 |
180
+ | "轮 B→C harness 换 id(无感知)" | 真实机制 = **DSH 会话是树,延续/fork 产生新节点**:header 带 `parentSession` + `seedLength`;`packages/core/session/src/index.ts` 有显式 `fork(source, boundary?, childSessionId?)`(注释:"Seeding with an existing event log replays/forks a session")。fork 对 UI 与上下文透明,故 agent/user 均感知为"同一对话" | 实测谱系:`345e1e12 →fork→ cb07ad10 (seed 23,388)`;`a1b3097d(根, 01:24) →fork(03:08)→ 328368a6 (seed 65,305) →fork(03:11)→ a61bbf92 (seed 65,308)`;`2f3a8234` 独立根 |
181
+ | 病根表述 | 修正为:**轮 B write(02:13:16/02:15:02,键 a1b3097d)与轮 C edit(03:41:38/03:53:15,键 328368a6)分属父子会话节点**,served 按 (session节点, path) 记账 → 子节点查空 → `E_RANGE_UNVERIFIED`。fail-closed、批量原子、恢复路径等结论不变 | 账本 sqlite 逐行时间核对 |
182
+ | "memory_flush 回显 session id 与账本键一一对应(轮 B=7104efad…)" | **不成立,观察源即错**。corti-memory `memory_flush` 回显的是 `exec?.session?.id`(`corti-memory/dist/index.js:381-384`,fallback `lastSeenSessionId`/"dsh-session"),**不是写账本的 `exec.agent?.session.id`**;7104efad 属 `/home/u1/workspaces/base`(workspace.json:base 桶 updated `09-07T18:13:19Z` = 本地 09-08 02:13;base 桶 `session-7104efad…/session.jsonl.zstd` 206KB,02:13–02:18 活跃),而账本 02:13 行键 = a1b3097d | 两个 id 分属不同 workspace 的会话对象 |
183
+
184
+ ### 7.2 write 语义与守卫归属(确认)
185
+
186
+ - better-dsh **未干预 write 执行**:上游 `packages/fs/tool-fs/src/write.ts` 自述 *"Model-facing full-file write … no policy means an unconditional atomic create-or-overwrite"*,确认信封 *"no file content is echoed back"*。better-dsh 仅经 `tools/post-execute` 追加锚点回放(模型面 content)。
187
+ - **整文件写在逻辑上不要求"之前见过"**:新文件天然无此前提;已有文件的守卫归覆盖确认层(DSH approval policy + `dsh-fs-observation-policy` 的 createIfAbsent/replaceIfVersion),与 hashline 无关。上文 §5 建议 4 中"write auto-read 锚点跨轮不可复用"的文案指引随 §7.3 裁决一并重写。
188
+
189
+ ### 7.3 hook 时机与回放必要性(裁决)
190
+
191
+ - `tools/post-execute` 即 **tool-result 相位**水漏(`pre-execute → execute → post-execute`;core/tools invariant:"post-execute must follow pre-execute or execute";spill-policy 同瀑布组合;签名 `(exec, result, next) → Promise<PostToolDecision>`,write-hook `await next()` 后追加内容,**异步安全**)。挂点选对了。
192
+ - 脆弱点:`final-result` 路径 **bypass post-execute**(core/tools `index.ts:421`)——未来异步/后台写工具若走该路径,回放会静默丢失。
193
+ - **回放与 edit 解耦(裁决)**:OMP write 模型面 = 成功确认 + 新 `[path#TAG]`,**不回显内容**(渲染器 12/6 行预览仅 UI 侧)。DSH write-hook 现回放全文件锚点预览(`fmtReadPreview` 默认上限 `DEFAULT_MAX_LINES=2000` 行、单行 200KB)——对整文件写是过度供给。裁决:write 结果**仅返回动作确认(+ 可选新 tag 头)**,内容核实由 agent 主动 read;write-hook 回放砍掉或降级为确认行。
194
+
195
+ ### 7.4 locator 去 session 化(裁决)
196
+
197
+ - 键 = **canonical 绝对路径(现成键,零新增)**:全部状态表本就以 `resolveTarget()` 的绝对路径为主键——`snapshots(path PRIMARY KEY)`、`undo(path PRIMARY KEY)` 现状即无 session;唯一 session 化的 `served` 去 `session_id` 列、PK 改 `(path)`,drift `reported` 列随行走。绝对路径单机全局唯一,集中单库不撞键。**不引入 CWD basename 前缀**(第三轮复核裁决):模型面 locator(行级 3-char 锚)永远伴随 edit 调用的显式 path 参数使用,不存在"裸 hash 找文件"的 surface,前缀列零收益——minimal effort 即删列。
198
+ - 全量哈希(xxh64 `contentChecksum`)仍是内容身份,现库 checksum 命中原样保留;跨会话共享同 path 的 served 行是期望行为(内容权威下"谁看过"无判定义务)。随键清理:`wipeServedState(sessionKey)` 删除;`SERVED_TTL_MS` 7 天 TTL 按 `updated_at` 照常 prune(`hash-store.js:488`)。迁移 = `HASH_STORE_VERSION` 6→7 重建 served 表;旧 12 个点目录弃置(TTL 反正清空,不作数据导入)。
199
+ - **§5 建议 3(改用 conversation/root id 作键源)作废**:host session id 引入 fork/子代理嵌套等不可控复杂度,被本节 path 键方案整体取代。建议 1(content-match 快道)与本节合流:内容即权威,served 出处账本对校验不再必需(undo/echo 归属另议)。
200
+
201
+ ### 7.5 存储布局:"到处拉屎"(裁决:集中化)
202
+
203
+ - 现状:`configDir(cwd)` 把 `hash-store.sqlite` 落在每个 workspace 下,实测 **12 个 workspace 各一份**(base 1.4MB、dashr 6.1MB+4MB WAL、superd/research/dsh-omp/browser-agent/temp/agent-harness/kb-dev/sidecarx/SoftEng/orchestra …)。
204
+ - 裁决(第三轮精化):迁移到 **`$DSH_HOME/storages/dsh-better-edit/hash-store.sqlite`**。`storages/` 是宿主状态数据区(workspace.json / message_feedback.json / session_projcache),语义对口;实测 `~/.dsh/plugins/dsh-better-edit/` 是安装脚手架位(README/code/cordis/minimal/standard),代码里原"无 cwd 兜底"指向 `plugins/` 属语义错位,一并修正。实现 = `configDir(cwd)` 删除 cwd 分支,统一走 `resolveDshHome()`(感知 `DSH_HOME` → 4999 测试线 `.dsh-test` 自动隔离)。
205
+
206
+ ### 7.6 修复批次合并
207
+
208
+ §7.2–§7.5 与原 §5 建议 1/2/4 合并为**一个 openspec change**(暂名 `hashline-content-locator`):内容寻址 locator + 集中存储 + write 回放解耦 + 多 tuple 失败文案修正。回归断言沿用 §6,另加:write→(fork 会话)→edit 应直接成功;`.dsh_better_edit/` 不再新建;上游 `~/.dsh` 下单库增长可观测。
209
+
210
+ ### 7.7 渊源备注
211
+
212
+ better-dsh vendored 头注自述 *"a dsh port of pi-hashline-edit-lsz"*(pi 系 = OMP 同族,即用户所称 "BetaEdit" 分支)。两支同源已分岔:OMP = 整文件快照 + 4-hex tag + session 级内存 store;DSH 分支 = 3-char 行锚(62³ 文件内唯一分配)+ (session,path) served 账本 + per-workspace SQLite。本次裁决实质是把 DSH 分支的校验权威从"会话出处"迁回 OMP 所在的"内容快照"一边,存储形态再叠加集中化。
213
+
214
+ ---
215
+
216
+
217
+ ## 8. 落地回执(v0.2.3b,2026-09-08)
218
+
219
+
220
+ §7 五项裁决已实现并实测:change `openspec/changes/2026-09-08-v0-2-3b-hashline-content-locator/`;
221
+ 单测 `test/hashline-store.spec.ts` 12/12;4999 第一人称实测见
222
+ `v0.2.3b-hashline-content-locator实测报告.md`。要点:中心库落位
223
+ `.dsh-test/storages/dsh-better-edit/`(served 表无 session_id 列)、write 无回放、
224
+ 内容快道在账本未见时直接放行;实测发现宿主 `dsh-fs-observation-policy` 的
225
+ `E_NOT_OBSERVED`(read-before-edit)为跨会话 edit 的另一道设计内守卫,予以保留。
226
+ 发布闸:a 实测 ✅ / b 报告 ✅ / c user 确认 ⏳。
@@ -0,0 +1,44 @@
1
+ # 上游 dsh 0.1.3-alpha.2 对齐轮本地实测报告(dashr 测试线)
2
+
3
+ - 实测日期:2026-09-08
4
+ - 范围:**仅 dashr(better-dsh 插件)** 自身对齐(omp-web 不在内);OpenSpec change `openspec/changes/2026-09-08-upstream-0-1-3-alpha-2-alignment/`(G1 S1–S7 + G2 小修,v0.2.3)
5
+ - 环境事实:checkout `upstream/deepseek-harness` @ `dsh-v0.1.3-alpha.2`(`82a5fd61a7`,release merge PR #3685);Node v22.22.1(unrun 路径);pnpm 11.7.0(store 重定向 `.scratch/pnpm-store`);better-dsh 副本 = canonical `0.2.3`(rsync 双向已收敛,src/test 字节一致);`DSH_HOME=.dsh-test`(prod `~/.dsh` 未动);web `127.0.0.1:4999`
6
+ - 上游版本判定(S0 报告 `docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-report.md`):rc.1 = 纯发布(代码零差);目标 = alpha.2(npm `alpha` dist-tag)
7
+
8
+ ## 实测结果表
9
+
10
+ | 步骤 | 结果 | 备注 |
11
+ |---|---|---|
12
+ | S1 侦察 | ✅ | 三 tag 本机拉齐;patch 载体 tag 间 diff 复核(tsdown.client.ts ±1 / pnpm-workspace.yaml +8−1;`resolveRepositoryRoot` 上游未吸收,仍需本地) |
13
+ | S2 切换与重放 | ✅ | alpha.5 脏 overlay 全量归档 `.scratch/alpha5-dashr-local-overlay.patch`(2527 行)→ stash → checkout alpha.2 → 三必需 patch 手工重放(unrun devDep / storeDir+verifyDepsBeforeRun+zeromq / tsdown `resolveRepositoryRoot`)。**overlay 定性**:其余 17 文件全部为 bun --compile 可移植补丁(注释自证;Node 下惰性/同行为),未重引(4999 走 Node),归档留存 bun 实验用 |
14
+ | S3 副本地化 | ✅ | 副本无 stale `dsh-client-runtime` peerDep;peer 范围含 alpha.2(0.1.3-alpha.2 ∈ `>=0.1.2-alpha.1 <0.2.0-0`);bundle 名 `@deepseek-ai/dsh-base`/`dsh-web-app` 不变 |
15
+ | S4/S5 安装与构建 | ✅ | `pnpm install`(**pnpm 11.7 自动插 `allowBuilds` 占位符 `set this to true or false` → 硬错,改显式布尔**,AGENTS.md 已知坑再现);`pnpm run build` 全绿 ×2(tsc host + tsdown + vite web + 224 client artifacts),better-dsh lib+client 在位 |
16
+ | S6 启动 4999 | ✅ | systemd-run `dsh-4999-test`;root 200;dump-config:`dashr-repl` 行 + `DASHR_KERNEL_PYTHON` 注入 ✓;**注**:stop 后立即 start 有 EADDRINUSE 竞态(旧实例端口未释放),需间隔 ~2s 或独立 unit 先验证 |
17
+ | S7.1 工具面 | ✅ | `tool-web` 在位;`str_replace_editor` **缺席(0)**(base/web-app 默认下架生效) |
18
+ | S7.2 client 卡片 | ✅ | boot 图含 `"id":"better-dsh"`;合并 loader bundle 200(5.2MB/224 模块),better-dsh 段与 `lib/client/index.js` **字节一致**(除 sourceMappingURL 尾行);单入口 `/plugins/??better-dsh/client.js` 404(alpha.2 只服务聚合 `??` 请求——探针改聚合提取法) |
19
+ | S7.3 行形状查表 | ✅(预查收敛) | ① connection 行形状未漂移(persona 行改 prefix/suffix 与 dashr 无交集);② `__DSH_TRANSPORT__` 读法不变(新增可选 `__DSH_CONNECTION_RECOVERY__`,缺省无害);③④⑤ 零改动 |
20
+ | S7.4 fence/isLoopback | ✅ 静态 | 页面含 `__DASHR_MOBILE__` boot script(web-trust 腿在位);`__DSH_TRANSPORT__` 0(空 authorities 惰性=设计);env 探针(`DSH_TRUSTED_HOSTS` 401/403)留手测 |
21
+ | S7.5 zoomGuard | ✅ 静态 | ④⑤ 零改动 → reconcile 路径不变;CDP 双形态留手测 |
22
+ | S7.6 telemetry | ⏳ 手测 | feedback 面已 revert(上游 13daefe073);`DSH_TELEMETRY_DISABLED=1` 立场 prod 验证 |
23
+ | S7.7 老会话读兼容 | ⏳ 手测 | `.dsh-test` 无 v1 历史会话可自动验证;建议 GUI 用旧会话验证(上游 released migration 兜底) |
24
+ | file-tool 升级矩阵 | ⏳ 手测 | 事件报告建议 3 回归项;本地链路 09-06 已过,本实例留 user GUI 复测 |
25
+
26
+ ## 发现与修复(本批次代码改动,vitest 全量 481/481 绿)
27
+
28
+ 1. **session 事件面 API 漂移(真实对齐点)**:registry 解析的 `@deepseek-ai/dsh-session@0.1.2-rc.1` 为 **format-v2 会话**(`SESSION_FORMAT_VERSION=0`;header 必填 `isSeeded:boolean`;无 `.events` 属性,改 `snapshotEvents()`/`ownEvents()`)→ dashr `url-schema/handlers/agent.ts` roster 的 `live.events` 属性读失效。修复:`live.snapshotEvents()`;测试 fixture 补 `isSeeded:false`(自包含 seed,非 fork)。见 diff。
29
+ 2. **pi-natives loader 锚点穿透**:`findPackageDir` 的 `createRequire.resolve` 快路径会经进程 globalPaths(含 pnpm `.pnpm/node_modules` hoist 根)从**任意锚点**命中已安装 addon,违反"锚点限定"契约(degradation 测试抓出)。修复:去掉 resolve 快路径,仅用自锚点向上的确定性 node_modules walk。见 diff。
30
+ 3. **escalation-guidance 附 A 定稿回填 canonical**(drift 修正):monorepo 副本已实装定稿(deniable/denied · per-call),canonical `src/index.ts` 仍旧句 → 回填;`presentation.spec.ts` 断言同步(定稿 token + 无 'retried once' 断言)。
31
+ 4. **附 C 矛盾子句结案(不暴露)**:render 证据(`renderReplBridgeInstructions` signature-only,description 散文不进模型可见面)→ 不做 dashr 侧订正;上游 schema 文本缺陷记上游面 Open item。S7 实证项(grep 装配产物 0 命中)未单独执行——本轮运行实例装配面即证(签名行、零散文)。
32
+
33
+ ## 环境注记 / Open items
34
+
35
+ - **peer 双副本分裂**:better-dsh 的 peer `@deepseek-ai/*` 经 pnpm autoInstallPeers 从 registry 装入 `.pnpm`(dsh-llm/dsh-session@0.1.2-rc.1),而部分依赖链解析到 workspace 副本 → **包级 `tsc -p tsconfig.json` 出现双身份类型分裂(69 错,dsh-llm branded 字段、SessionSeq 品牌)**;权威类型门 = 根 `tsc -b tsconfig.host.json`(`pnpm run build`,**全绿 0 错**)。vitest 全量(36 文件/481 用例)绿。非代码回归,记录待解(历史 dev-loop 在 alpha.5 未现,系本次 registry 解析到 rc.1/alpha.2 后拓扑变化)。
36
+ - **registry 发布物与 tag 疑点(上游面)**:npm `@deepseek-ai/dsh-session@0.1.2-rc.1` 实为 format-v2 API,而 git rc.1 tag(== alpha.5)按版本号-only 判定应无此改动 → 疑似 session 包独立发布线带 0.1.3-alpha 代码却标 rc.1。记录,prod 升级前需以 npm 发布物为准复验。
37
+ - 手测/用户项:老会话读兼容、fence env 探针(401/403)、zoomGuard/mobile CDP 双形态、file-tool 升级矩阵、prod 3080 复测(write 事件回归)。
38
+ - 版本:canonical `dashr/package.json` → **0.2.3**(本地 commit+tag `v0.2.3`;npm 发布按 AGENTS.md 红线另行)。
39
+
40
+ ## 产出
41
+
42
+ - S0 差异报告:`docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-report.md`(2026-09-08 已落)
43
+ - 本报告 + OpenSpec change `2026-09-08-upstream-0-1-3-alpha-2-alignment/`(proposal/design/tasks/specs delta)
44
+ - overlay 归档:`.scratch/alpha5-dashr-local-overlay.patch` + 逐文件分片 `.scratch/overlay-triage/`
@@ -0,0 +1,110 @@
1
+ # 上游 dsh 0.1.2-alpha.5 / rc.1 → 0.1.3-alpha.2 差异调研报告
2
+
3
+ - 调研日期:2026-09-08(本机复核日;上游 rc.1 发布 2026-09-03、alpha.2 发布 2026-09-07T13:11Z)
4
+ - 方法:**clean source git diff,非 npm tarball**。本机 `upstream/deepseek-harness` checkout 拉取 `dsh-v0.1.2-rc.1` / `dsh-v0.1.3-alpha.1` / `dsh-v0.1.3-alpha.2` 三个 tag 后,以 tag 树间 diff + 仓内 `.agents/notes/implemented/` 设计笔记 + npm registry 事实三源互证。文件/行数口径:`dsh-v0.1.2-alpha.5` vs `dsh-v0.1.3-alpha.2`(rc.1 与 alpha.5 代码零差,等价);commit 数口径:`rc.1..alpha.2`(rc.1 是 alpha.2 的祖先,范围线性、无歧义)。
5
+ - 版本事实:
6
+ - **rc.1 = release-only**:`alpha.5..rc.1` 只有 2 个 commit——release commit `a66e470204`(`release(dsh): 0.1.2-rc.1`,2026-09-03)+ 1 个空 merge(`5a69ba1cdd` Merge PR #3445,把 alpha.5 发布分支并入主线);代码差**为零**(见专题一)。npm `latest`/`next` dist-tag 均指 `0.1.2-rc.1`——今天裸装 `@deepseek-ai/dsh` 拿到的就是 alpha.5 代码。
7
+ - **alpha.1 = 只 tag 未上 npm**:`dsh-v0.1.3-alpha.1` tag 存在(merge PR #3554,09-04),但 registry `versions` 里 **没有 0.1.3-alpha.1**,0.1.3 线从 alpha.2 起;`alpha` dist-tag = `0.1.3-alpha.2`。
8
+ - alpha.2 tag = merge PR #3685(worktree/release-dsh-0.1.3-alpha.2,09-07);rc.1 与 alpha.1 均为其祖先(rc.1..alpha.1 = 328,alpha.1..alpha.2 = 316)。
9
+
10
+ ---
11
+
12
+ ## 数字复核表(转述值 → 实测值)
13
+
14
+ 对来源总结逐项复核,两处需修正口径,其余全部精确命中:
15
+
16
+ | 来源总结 | 实测(本机复核) | 结论 |
17
+ |---|---|---|
18
+ | rc.1 落后 alpha.5 "1 commit" | `alpha.5..rc.1` = 2 commits(release `a66e470204` + 空 merge `5a69ba1cdd`) | 口径差异;结论不变——代码零差 |
19
+ | rc.1 vs alpha.5 "252 文件全 package.json 版本号" | 252 文件,**0 个非 package.json**,diff 行过滤 `"version"` 后**零残留** | ✅ 精确命中 |
20
+ | alpha.2 "417 commits" | `rc.1..alpha.2` 共 **644** commits = 227 merges + **417 非 merge**(first-parent 仅 76) | 417 = 非 merge 数;总数 644 |
21
+ | "~103k+/40k− across 4.8k files" | 4,866 files,**+103,306 / −39,537** | ✅ 命中 |
22
+ | "~59k+/19k− 真实代码(剔 notes/snapshots/docs)" | 仅剔这三前缀 = 1,974 files,**+84,482 / −27,248**——到不了 59k | ❌ **不可复现**(见下"剔除阶梯") |
23
+ | fix 178 / test 97 / docs 53 / ci 11 / perf 9 | 对 644 条 subject 前缀统计:fix **178** / test **97** / docs **53** / ci **11** / perf **9**(另有 refactor 27 / feat 21 / chore 13 / revert 2 / release 3 / merge+other 230) | ✅ 全命中 |
24
+ | rc.1 = latest/next;alpha = alpha.2 | `npm view dist-tags`:latest=0.1.2-rc.1, next=0.1.2-rc.1, alpha=0.1.3-alpha.2 | ✅ |
25
+ | alpha.1 从未上 npm | `npm view versions` 无 0.1.3-alpha.1 | ✅ |
26
+
27
+ **真实代码量的"剔除阶梯"(都可精确复现)**:ALL 4,866 files +103,306/−39,537 → 剔 `.agents/notes/`+`snapshots/`+`docs/` 1,974 files +84,482/−27,248 → 再剔 `pnpm-lock.yaml` 1,973 files +83,369/−27,204 → 再剔各包 `tests/`·`test/`·`__snapshots__` 1,366 files +38,948/−11,653。来源总结的 ~59k+/19k− 落在前两档之间,对应的剔除集本机未能还原(可能含更大的生成语料剔除),**引用时以阶梯为准,勿再用 59k 口径**。
28
+
29
+ 主题占比(包组级,alpha.5..alpha.2,实测):`session*`(session 组全部 8 包)211 files **+25,333/−10,849**;`client*` 358 files +10,871/−2,691;`apps/web` 95 files +2,423/−641;`subagent` 75 files +3,144/−1,955;`api` 64 files +3,695/−985;`core` 71 files +2,919/−1,861;`llm` 68 files +3,011/−493。与来源"dominant areas = client/session/subagent/core/llm/api/apps/web"一致,session 显著居首。
30
+
31
+ ---
32
+
33
+ ## 专题一:rc.1 是一个纯发布 commit——bump 过去只买一个标签
34
+
35
+ `git diff dsh-v0.1.2-alpha.5 dsh-v0.1.2-rc.1`:252 个变更文件全部是各包 `package.json`,且 diff 内容除 `"version"` 字段外零残留(无 lockfile、无源码、无配置)。rc.1 相对 alpha.5 **功能字节级一致**。
36
+
37
+ 含义:**以 rc.1 为目标 = 白动**(只换 dist-tag 外观)。真正的代码分水岭在 `dsh-v0.1.3-alpha.2`。若只想要"稳定标签",rc.1 与 alpha.5 无差别;若需要 0.1.3 线的修复,只能走 alpha 通道(alpha.2 起)。
38
+
39
+ ---
40
+
41
+ ## 专题二:session format v2 + 内嵌 assistant 流(落盘/回放契约变更)
42
+
43
+ **三个 breaking commit 全部落在 omp-web 桥接的会话持久化/回放接缝上**(均实测在 `rc.1..alpha.2` 范围内):
44
+
45
+ | commit | subject(实测原文) | 性质 |
46
+ |---|---|---|
47
+ | `f99b06eaed` (09-01) | `feat(session)!: embed assistant streams in format v2` | 断(格式面) |
48
+ | `d1521ea783` (08-31) | `feat(session)!: add released format migration` | 断(迁移面) |
49
+ | `bec6805d6a` (08-28) | `refactor(session-persistence)!: handle-based seam with a lifecycle-owned write path` | 断(写路径面) |
50
+ | `c58097a826` (08-31) | `feat(session-persistence-jsonl): cross-process write-ownership lease` | 紧邻(lease 即 `session-persistence-omp.ts` 直接对话对象) |
51
+
52
+ 配套证据(tag 树内设计笔记,路径均已核实):
53
+ - `implemented/architecture/2026-09-01-v2-embedded-assistant-streams.md`(v2 内嵌流设计)
54
+ - `implemented/architecture/2026-09-05-read-only-session-migration-preparation.md` + `2026-09-06-embedded-stream-record-readers.md`(回放侧按 compact record 读内嵌流)
55
+ - `implemented/feature/2026-08-31-cross-process-session-write-lease.md`、`implemented/simplification/2026-08-30-jsonl-only-session-persistence.md`(jsonl-only 收敛 + 跨进程写权)
56
+ - 新包齐备:`packages/session/` 下新增 `session-format`、`session-format-catalog`、`session-format-v0-to-v1`、`session-format-v1-to-v2`(src:codec/dispositions/migration/validation)、`session-log-deepseek`、`session-checkpoint-policy`;`session-persistence-jsonl/src` 现含 `lease.ts`/`migration-verifier.ts`/`format.ts`/`generation.ts`/`storage.ts`/`worker.ts`。迁移测试语料是全 diff 最大单体(`v1-to-v2/tests/migration.spec.ts` +1,251)。
57
+
58
+ 流/回放修复链(同簇,非断):`30e045dfad` `feat(agent): emit live assistant stream frames`;`165cc31eb8` `perf(llm,host): read embedded Assistant streams per compact record`;`7bab91d247` `fix(llm): retain Anthropic resolved model during replay`。
59
+
60
+ ---
61
+
62
+ ## 专题三:persistence 重构主题(读/写/迁移的 perf 潮)
63
+
64
+ 除专题二四枚外,`session-persistence*` 在本范围有一整条 perf 链:prepared reads before publication、streamed migration publication/verification、restore 冻结读(来源总结定性);`2026-09-04-session-open-performance-gate` 测试笔记佐证"打开会话性能门槛"成为一等关注。对桥接方的含义集中在专题二列出的断点 + lease——即"谁持有某 jsonl 写权、生命周期何时移交、跨进程并发写如何排他",这正是第三方持久化实现需要对齐的契约。
65
+
66
+ ---
67
+
68
+ ## 专题四:其余主题簇(非断,但升级需知)
69
+
70
+ | 簇 | 证据(实测 subject,均 in-range) | 备注 |
71
+ |---|---|---|
72
+ | **str_replace_editor 默认下架** | `36a4665144` `feat(base): remove str_replace_editor from default tools`;`965adbb5cf` `feat(sdk): disable str_replace_editor by default` | 笔记 `2026-09-05-base-default-file-editor`;升级后默认工具面少一个 editor |
73
+ | **outbound 全走 proxy** | `545e2ad914` `feat(net): route every outbound request through the configured proxy` | 未配 proxy 时无行为变化 |
74
+ | **read_image 图片卡** | `56ca8af0ee` / `a4d4404708`(ui-tool 渲染嵌套/直接 read_image 结果为图) | 前端渲染面 |
75
+ | **通用文件存储 + 提交生命周期流式** | `bafa6ae11d` `feat(conversation): stream files through submission lifecycle` | 附件面 |
76
+ | **agent/UX** | `48cc1cf1d6` `feat(agent): announce model switches`;`96ead6091d` `feat(subagent): align human inbox controls`;`040d73871b` `feat(agent-team): unify messages on steer` | |
77
+ | **message-edit:landed 又 revert** | `ef88756f13`(09-02)`feat(session, agent, web): support same-session message editing` → `e974a655a0`(09-02)`Revert "…"` | 同日往返;**别按该 feat 写适配** |
78
+ | **telemetry/feedback:default-on 又回滚** | `02a029e679` feedback 记录进 session log、`9ffe85a512` OTel 默认全量上传统计 → `13daefe073` `revert(session-telemetry-otel): leave telemetry on its own transport` | 笔记 `2026-09-05-canonical-feedback-log` / `nonofficial-feedback-otel`;prod 跑 `DSH_TELEMETRY_DISABLED=1`,升级时查 session log 是否开始夹带 feedback 记录 |
79
+ | 其余重churn | fix 178 / test 97 / docs 53 / ci 11 / perf 9;web-heavy(sending states、skill chips、terminal cards、session reveal) | 体积主导:`.agents/notes`(含 notes i18n.yaml 迁移与 archived manifest.json 重组,~2.4k files)+ session/client 组 |
80
+
81
+ ---
82
+
83
+ ## 对本线(omp-web 桥 + dashr 测试线)的含义
84
+
85
+ 1. **对 omp-web 不是 drop-in bump**。format v2(`f99b06eaed`)+ released migration(`d1521ea783`)+ handle-based seam(`bec6805d6a`)+ 写权 lease(`c58097a826`)恰好是 `session-persistence-omp.ts` / `replay.ts` 桥接的契约面。升级 = 一次 upstream-alignment 适配轮:diff 载体 → 新格式读写 + lease 语义对齐 → 迁移读兼容验证 → 冒烟,按所在仓的 AGENTS.md 节奏(dashr 侧即 S1–S7)。
86
+ 2. **别追 alpha.1**:npm 无该版本,registry 线从 alpha.2 起;本地 tag 在库即可,发布侧不存在。
87
+ 3. **dashr 自己的 4999 测试线(现 alpha.5)将来对齐 alpha.2 时的 S7 盯点**(本轮不执行):默认工具面少 `str_replace_editor`(工具清单对比);session 落盘格式迁移(老 session 读兼容靠上游 released migration,验收一条老会话打开);`__DSH_TRANSPORT__`/`connection` 行/`AppFrame` 语义属性的行形状是否被 web-heavy churn 漂移(upstream-alignment.md S7.5 三处 + S7.8 两处查表在切换轮现查);telemetry 默认面变化对 `DSH_TELEMETRY_DISABLED=1` 立场的验证。
88
+ 4. **数字口径提醒**:引用本报告数字时用复核表/剔除阶梯的实测值;"417 commits""~59k real code"两个转述口径已修正。
89
+
90
+ ---
91
+
92
+ ## 参考索引
93
+
94
+ | 主题 | 证据 |
95
+ |---|---|
96
+ | rc.1 release-only | commit `a66e470204`;`git diff alpha.5 rc.1` = 252 package.json,版本号-only |
97
+ | format v2 / 内嵌流 | commit `f99b06eaed`;笔记 `implemented/architecture/2026-09-01-v2-embedded-assistant-streams.md` |
98
+ | released 迁移 | commit `d1521ea783`;包 `packages/session/session-format-v1-to-v2/`(codec/migration/validation) |
99
+ | handle seam | commit `bec6805d6a`;`session-persistence-jsonl/src/{lease,storage,worker,migration-verifier}.ts` |
100
+ | 写权 lease | commit `c58097a826`;笔记 `implemented/feature/2026-08-31-cross-process-session-write-lease.md` |
101
+ | jsonl-only 收敛 | 笔记 `implemented/simplification/2026-08-30-jsonl-only-session-persistence.md` |
102
+ | 回放读内嵌流 | commit `165cc31eb8`;笔记 `implemented/architecture/2026-09-06-embedded-stream-record-readers.md` |
103
+ | 迁移只读准备 | 笔记 `implemented/architecture/2026-09-05-read-only-session-migration-preparation.md` |
104
+ | message-edit 往返 | commit `ef88756f13` / revert `e974a655a0`(均 09-02) |
105
+ | telemetry 往返 | commit `02a029e679`/`9ffe85a512` → revert `13daefe073`;笔记 `…/2026-09-05-{canonical-feedback-log,nonofficial-feedback-otel}.md` |
106
+ | editor 下架 | commit `36a4665144` + `965adbb5cf`;笔记 `simplification/2026-09-05-base-default-file-editor.md` |
107
+ | proxy 全覆盖 | commit `545e2ad914` |
108
+ | 图片卡 / 文件流 | commit `56ca8af0ee`/`a4d4404708`/`bafa6ae11d` |
109
+ | agent/UX | commit `48cc1cf1d6`/`96ead6091d`/`040d73871b` |
110
+ | 体积与类型 | 复核表 + 剔除阶梯;session 组 +25,333/−10,849 居首 |
@@ -0,0 +1,73 @@
1
+ # v0.2.3b hashline-content-locator 实测报告
2
+
3
+ > 2026-09-08 · 环境:DSH 4999 dev 线(upstream checkout `0.1.3-alpha.2-82a5fd6-dirty`,
4
+ > `DSH_HOME=~/.workspaces/dashr/.dsh-test` 语义路径 `.dsh-test`),better-dsh vendored
5
+ > hashline 桥(change `2026-09-08-v0-2-3b-hashline-content-locator`)。
6
+ > 前置:诊断报告 `2026-09-08-hashline-edit-E_RANGE_UNVERIFIED跨轮会话键失效-诊断报告.md` §7。
7
+
8
+ ## 0. 一句话结论
9
+
10
+ 五项裁决全部落地并实测通过:served 账本去 session 化(schema v7,path 主键)、集中存储
11
+ `$DSH_HOME/storages/dsh-better-edit/`、verifyServedRange 内容快道、write 回放移除、失败文案
12
+ 修正。单测 12/12 新增全绿、全量 491 过 2 挂(挂项为先于本改动存在的上游类型漂移,stash 基线
13
+ 复跑证实);4999 真实 agent session 第一人称实测:write 只回确认信封(无锚点回放)、read 记账
14
+ 进中心库(served 表 `path,hashes,reported,updated_at`,无 session_id 列)、跨会话 edit 链路
15
+ 走通、全程无任何 `.dsh_better_edit` 点目录。
16
+
17
+ ## 1. 单元层(canonical,`npx vitest --run`)
18
+
19
+ - 新增 `test/hashline-store.spec.ts` **12/12 绿**:v7 中心库落位与无点目录断言、path 主键
20
+ upsert 合并、跨"会话"上下文免 read 取行(fork 模拟)、drift 标记随 path、v6→v7 迁移重建
21
+ ( handmade 旧库打开后 `session_id` 列消失、version=7)、TTL prune、verifyServedRange 六态
22
+ 矩阵(账本未见→内容快道通过;账本全知匹配→过;内部漂移→`E_RANGE_STALE`;账本洞→
23
+ `E_RANGE_UNSERVED`;单边账本→`E_RANGE_UNVERIFIED`;拒绝文案含"Read the file once…
24
+ resubmit the whole batch")。
25
+ - 全量:**491 passed / 2 failed**,2 挂项(`handlers/agent.ts` `snapshotEvents`、
26
+ `SessionHeader.isSeeded`)经 `git stash` 基线复跑**证实先于本改动存在**(上游 alpha.2 对齐
27
+ 类型漂移,非本 change 引入;tsc 同样仅此 3 条既有错)。
28
+
29
+ ## 2. 构建(monorepo 副本)
30
+
31
+ - rsync(AGENTS exclude 表)→ `pnpm --filter better-dsh exec tsdown` → **9 files 529.98 kB**,
32
+ `lib/index.js` 形态正确 → `tsx scripts/build-client.ts` → `lib/client/index.js`
33
+ 17.82 kB(md5 `a88850ec056aae943934adfdeb79347e`,client 半零改动)。
34
+ - **教训(新增坑,已亲历)**:直接以 `node_modules/.bin/tsdown` 裸跑(不经 pnpm workspace env)
35
+ 会产出 `.mjs` 形态(7 files),boot 即 `ERR_MODULE_NOT_FOUND lib/index.js`——**必须用
36
+ `pnpm --filter better-dsh exec tsdown`**;且 `--delete` rsync + tsdown clean 会连带重置副本
37
+ 配置与 lib(本次已重建,副本 package.json/tsdown.config 现与 canonical 一致)。
38
+
39
+ ## 3. 4999 第一人称实测(真实 agent session,CDP 驱动 GUI)
40
+
41
+ 工作区 `/home/u1`(GUI 工作区选择器未绑到 ws-probe,落在默认 home 桶——不影响断言),
42
+ 模型 DeepSeek-V4-Flash。三段剧本:
43
+
44
+ | 步 | 动作 | 断言 | 结果 |
45
+ |---|---|---|---|
46
+ | S1 | write 创建 `probe-v023b.md`(6 行规格内容) | write 结果**无** `--- Auto-read (hashline anchors) ---` 段,仅确认信封 | ✅(transcript 仅 "Produced probe-v023b.md") |
47
+ | S1 | read 全文 | 锚点记账进**中心库** | ✅ `.dsh-test/storages/dsh-better-edit/hash-store.sqlite`(+WAL)出现;served 表列 `path,hashes,reported,updated_at`,**无 session_id**;单行 `path=/home/u1/probe-v023b.md → ["Udx","nyO","zlr","DBc","Zuy","GTH"]` |
48
+ | S2(**新会话**,不同 session id,从未观察/读过该文件) | 直接 edit(锚点 zlr/zlr 来自 S1) | 旧代码此处必 `E_RANGE_UNVERIFIED` | ⚠️ 见 §4——被**宿主层另一道闸**前置拦截 |
49
+ | S2 | read 一次后同参数 edit | 成功落盘 | ✅ 文件第 3 行 = `beta line EDITED-BY-v0.2.3b-PROBE`;无任何 E_RANGE_* |
50
+
51
+ 存储探针:`/home/u1/.dsh_better_edit` **不存在** ✓(home 即 workspace,dot-dir 若仍会创建必然在此)。
52
+
53
+ ## 4. 实测发现:宿主 fs 观察策略是跨会话 edit 的另一道(设计内)闸
54
+
55
+ S2 盲 edit 返回 `[E_NOT_OBSERVED] The file has not been observed in this session
56
+ (read-before-write policy). Call read() first` ——这是 **`dsh-fs-observation-policy` 宿主插件**
57
+ 的 observed-state 守卫(owner=agent 会话的 WeakMap,read/write/edit 记版本),**不是** hashline
58
+ served 账本。定性:
59
+
60
+ - **保留**(user 裁决语义:"安全守卫在确认覆盖/见过"——read-before-edit 正是宿主层的"见过");
61
+ 本 change 的范围是把 hashline 层的**重复**session 账本拿掉,不是拆宿主守卫。
62
+ - 事故剧本(S1 write → fork 子会话 edit)在原线上之所以先报 hashline 错,是 fork 继承了观察态;
63
+ hashline 层修复后,**同进程 fork 续聊**的 edit 将直接由内容快道放行(fork 不再需要冗余 read)。
64
+ - 全新无关会话(如本 S2)盲 edit 被宿主拦——符合"读过才许改"的常理,非回归。
65
+
66
+ ## 5. 残留与移交
67
+
68
+ - 预置 guidance 覆盖目录现种子于 `storages/dsh-better-edit/`(ensurePresetGuidance 同目录)——
69
+ 与 store 同居一处,属可接受的组织形态。
70
+ - 旧 12 个 `<workspace>/.dsh_better_edit/` 弃置未删(TTL 语义自然清空;物理清理留 user 决定)。
71
+ - 4999 实例已停本探针产物:`/home/u1/probe-v023b.md` 与 `.scratch/ws-probe/` 已清理。
72
+ - **发布闸状态**:a 第一人称实测 ✅ → b 本报告 ✅ → **c user 明确确认放行 ⏳**(单次有效;
73
+ 未 commit、未 tag、未 publish)。