|
|
e0f7f2477c
|
修复: 验证脚本在 dsh 0.2.0 下直接崩掉(ERR_MODULE_NOT_FOUND),且漏认 v4 会话文件
升级到 0.2.0-rc.2 后 `verify-mail-sessions-readable.mjs` **一条都验不了**:
1) 依赖不再嵌在 `<dsh>/node_modules/@deepseek-ai/`,而是平铺到
`/usr/lib/node_modules/@deepseek-ai/` ⇒ import 直接 ERR_MODULE_NOT_FOUND;
2) 会话文件新增 **v4**(`session.v4.jsonl.zstd`),walk 只认 v0+v3
⇒ 本条线索的 `mail-f8f9a840`(v3+v4 共存、**无 v0**)会被**整目录漏掉**。
★ 关键点:这两种失败都长得像"会话不可读",但**都不是**。
1 是脚本自己崩了(连候选数都出不来);2 是**漏扫**(少算而不是算错)。
—— "工具报错"与"数据坏"必须分开,否则会把脚本的年龄当成磁盘的病情。
## 实测(0.2.0-rc.2 修好后)
--prefix mail- : 候选 55 可读 55 不可读 0 <- 邮件通道全绿
mail-f8f9a840(.new 线索): 可读,1623 events
mail-d042cc4c(老线索) : 可读,56012 events
全盘 /root/.dsh/sessions : 候选 148 可读 119 不可读 29
那 29 个不可读**全部**是既有的独立缺陷
(`subagent/descriptor ... unsupported descriptor version 2`,去重后仅此一种),
**没有一个是 mail-*** ⇒ 与邮件通道无关,仍不建议混进同一个 repair。
修法:先探两个候选根再 import(不靠报错"感觉"哪个对),
并把 v4 加进 SESSION_FILES。
|
2026-10-02 04:05:04 +08:00 |
|
|
|
65de1c3884
|
跨端对齐:授权栏 navigator_only + 组件按页拆分 + 服务器补 permission_options
用户两项裁定落地(均为 ask_user 明确选择):
① 授权栏口径 = navigator_only(照 WebUI 架构)
· 新建 pages/PermissionPanel.ets —— 详情页的决策面板,
对应 MailView.tsx:693 的 PermissionPanel(审批型 / 主动提问 / 已处理 三态)
· 决策入口从授权栏移到 MailDetailPage;MailDetailPage 原来只显示一个
「权限请求」小标签、根本没有决策入口(比 WebUI 少一整块,且反了:
栏里能决策、点进详情反而不能)
· PermissionTab 删掉内联「同意/拒绝」+ 备注框 + decide():
整卡可点 → onOpenMail(对齐 WebUI PermissionList.tsx:81 的 pick())
· PermissionRequest 补 source_account_id(客户端侧记来源,跳详情要定位网关)
② 服务器补 permission_options —— 修一条真实的、跨端共有的缺口
· mails.permission_options 从 INSERT 起就写进去,但**从来没有任何读路径
选过它** ⇒ 详情端点永远返回空。WebUI 的决策面板读 mail.permission_options,
所以提问型的预设选项**两端全部落空**(审批型靠 ['同意','拒绝'] 兜底蒙混)
· GetMailByID 补选该列 + JSON 反序列化(与 cc_list 同款)
③ 组件按页封装(用户要求「以便与 WebUI 一一对应」)
MainPage.ets 4592 → 3192 行
· pages/PermissionTab.ets 720 行 ↔ PermissionList.tsx
· pages/ContactsTab.ets 796 行 ↔ ContactPanel.tsx
· pages/NavDestinations.ets 181 行 ↔ Navigation 壳
· pages/NavShared.ets 65 行 ↔ 跨栏共用件
④ 判据跟着组件搬家(否则静默失效,不是红)
harmony-logic 的 pageCode / harmony-nav 的 navSrc 改为显式文件名单;
harmony-appearance 的 PANE_SOURCES 补 ContactsTab;harmony-contacts 三个
test 并入 ContactsTab;harmony-logic 的决策断言改指 PermissionPanel,
并新增「授权栏不许再有内联决策」两条(navigator_only 的正形状)。
animation-audit:共享元素转场判据从「同文件共址」改为「按 id 找驱动」。
旧形状把 in/out 端必须在同一文件当成代理,而两端**天然在两处**;
抽出写信页(NavDestinations 持有 in 端)后误报。新判据仍要求每个 id
都有 Motion.morph 驱动 —— 变异实测:把驱动换成裸 animateTo 仍判红。
判据:files=34 checks=556 red=1(仅 build-stamp,产物待重构建)
|
2026-09-24 10:10:32 +08:00 |
|
|
|
176c90272b
|
补充: 「差集」有**两个**成员(不是 §18.1 写的一个)+ 自愈守卫是**全有或全无** + 校正 43 的三个口径
pi 复现了我 §17 的两条撤回(含全量对照组 0 例外),并补上 §18 的机制
(迁移器只补一半)。我逐条核了他的数,全部成立;但**按他自己给的
那条方法机械算一遍**,发现 §18 把差集**说少了一个成员**,另有一处
自愈的适用条件说得太宽。本提交是他那封的**同一条方法的下一次应用**。
## 一、§18.1.1 差集是 2 个成员,不是一个
方法 = 「校验器要求 id」−「迁移器自愈 id」。机械枚举:
VALIDATED: agent/inbox/spliced, assistant/message,
session/title-llm-request, tool/result, user/message
HEALED : assistant/message, tool/result, user/message
⇒ 差集 = { agent/inbox/spliced, **session/title-llm-request** }
第二个成员同样"校验要 id、迁移器不补"(messageValue 经 exactRecord+
nonEmptyString(id);normalizeLegacyMessage 无此分支)。实测只剥它的 id:
transformed → refuses ... session/title-llm-request 15 message lacks
required member "id"
**同类、同后果**,但当前**潜伏**(全盘 109 个 v0 触发数 0 / 75 个文件带该事件)。
⇒ §18.2「只补 inserted 就够了」在当前数据上**仍然成立**,但成立的理由
**比 §18.1 写的窄**:不是差集只有一个成员,而是第二个恰好没被触发。
repair 脚本覆盖的是差集的 1/2 —— 若哪天 title-llm-request 丢 id,
**报错一模一样而脚本覆盖不到**。脚本头注释已写明该边界。
## 二、§18.1.2 自愈的前提是「全无」,不是「缺 id」
index.js:2179 的守卫是**全有或全无**:id/role/message 三个都不在才补。
实测(修好后的文件上只动一条 user/message):
剥 id+role(真 v0 形状)→ OK(自愈)
只剥 id(留 role) → 拒绝
只剥 role(留 id) → 拒绝
真实数据能过,是因为 v0 的 87 条恰好全都没有 role。
⇒ 准确说法是"迁移器会给**完整的 v0 形状**补 id",半成品不在自愈范围。
## 三、校正「43」的三个口径(§18.2 表 + §12)
· 被修的 v0 artifact : 43 = 40 mail-* + 3 非邮件
· --prefix mail- 验收候选 : 43 = mail-* 会话文件(含 v3)
· mail-* 目录名 : 41
前两个都等于 43 **但不是同一个集合**(被修集合的 3 个非邮件,在验收
集合里换成 v3-only 的 mail-f8f9a840)。数字相同 ≠ 集合相同。
另核:.bak 共 80 个 v0(37 文件 ×2 + 6 ×1 = 80),去重后 43 —— 与 pi 一致。
## 四、我独立复现的最强对照(与 pi 一致)
从未修过的 v0 共 66 个:两档都 OK 37 / 两档都 FAIL 29(全是
subagent/descriptor v2)/**transformed OK & current FAIL 0**。
⇒ 那个组合确系测量产物。§18.3(id 只查 nonEmptyString)我读码确认。
未改动 pi 的 §18 正文;新增 18.1.1 / 18.1.2 与三处口径标注。
|
2026-09-19 12:47:32 +08:00 |
|
|
|
45577aa3b6
|
更正: 脚本注释里那条 别补 user/message 也是假象 —— 迁移器本来就会给它合成 id
与 §17 同源:后半句依据的是 校验污染入参后再校验 的假象。
深拷贝重测 40/40:补与不补都 strict-ok。
|
2026-09-19 12:16:08 +08:00 |
|
|
|
fe0cfe626c
|
验收口径: --prefix 严格前缀 —— 修正 --only mail- 误匹配 agentmail- 造成的 3 个假失败
第一版验收用 --only mail- 得到「47 可读 / 3 不可读」,但那 3 个根本不是邮件会话:
目录名是普通 UUID,只是父目录 --home-program-agentmail-- 里含子串 mail-。
它们的错因是另一个独立缺陷(subagent/descriptor v2)。
换严格前缀后的真实数字:真 mail-* 会话 43/43 可读,0 不可读。
脚本现在同时提供 --only(子串)与 --prefix(目录名严格前缀)。
|
2026-09-19 12:04:43 +08:00 |
|
|
|
b4a8f74ae5
|
修复: dsh 邮件通道全断的**两侧**根因(桥侧不产 message id 是真正在写的那一处)
现象:dsh 的邮件通道全断。老会话读不出来 ⇒ 桥报 SessionQueryError ⇒ 按"不在磁盘"
处理 ⇒ 再 create 撞 `already exists`。修好读路径之后又立刻暴露下一层
`message "undefined" is already pending`。
根因一(历史数据,dsh 侧):v0 会话的 `agent/inbox/spliced.inserted[]` 缺 `id`/`role`,
v0→v1 迁移第一步就拒绝。40 个真 mail-* 会话全部命中。
根因二(**仍在写**,本仓侧):`plugins/dsh-mail-bridge/lib/message.js` 的
`userMessage()` 只产出 `{content, source}`。DSH 0.1.5 的 inbox 按 `message.id` 去重
(`dsh-agent-loop` 的投影 apply() 与 mutate() 各维护一个 Set),id 全是 undefined
⇒ **第二条消息必挂**。日志里最早的同类记录在 2026-09-07,累计 50+ 次。
官方形状在 `@deepseek-ai/dsh-llm` 的 `createMessage()`({id, role, content, source}),
同一份 dsh 里其它插件都用官方的 createUserMessage(),只有这个桥手搓。
以前没炸是因为读路径先坏,根本走不到 followup。
本次改动
- message.js/.d.ts: userMessage() 补 id: randomUUID() 与 role:'user'
- test/message.test.mjs: 钉住「id 非空」「两条消息 id 必须不同」,用官方 inbox
去重逻辑逐字复刻验证(修复前 message "undefined" is already pending,修复后 20 封全唯一)
- scripts/: repair-legacy-spliced-ids.mjs(v0,默认 dry-run)、
repair-v3-usermessage-ids.mjs(v3)、verify-mail-sessions-readable.mjs
(走生产真读路径 JsonlSessionPersistence.open,而非解码器口径)、两个 apply driver
- docs/DSH-0.1.5-MAIL-CHANNEL-ROOTCAUSE.md: 补执行结果与两处新事实
执行与验收(详见文档 §9-§15)
- v0 修 40 个、v3 修 2 个;逐文件解压后与备份 `cmp` **逐字节相等**,事件数 40/40 一致,
零丢失(25.2MB→12.5MB 是单帧改 500 行/帧的重压缩,不是丢数据)
- 真 mail-* 会话最终 **41/41 可读**
- journal 里同一会话从 `already exists` 变为 `resume 续谈`,且持续增长
(22647→22685 事件),最新 user/message 带真实 UUID;修复上线后 already pending 计数为 0
- 已在生产部署(deploy/redeploy-plugin.sh dsh,快照+原子软链+重启+后置验证全绿)
两个必须记住的坑
1. **校验与落盘不能共用同一批对象**:createRestore().decodeRow() 会原地改写入参
(补全 dt 数组),污染后写出去会报 `released Session row N has seq gap`。
这曾让 dry-run 说"40 个可修"、apply 只说"3 个"。
2. **判定磁盘健康只认 open()**:readSession() 走 SessionCorpus.load,命中有 live 会话时
直接返回内存快照、不校验磁盘;open() 才走 validateStoredEvents。两条路径结论相反
是设计使然,不是矛盾。
|
2026-09-19 12:03:34 +08:00 |
|
|
|
c7cb88d9aa
|
chore(security): 轮换 verify-l2.sh 里那把曾进 git 历史的 user key
旧值(jianf 的 user key,1759666 起在历史里)已从库里删除,实测
`/auth/me` → **401**,即暴露在历史中的那串现在无效。新值只存在
/root/gotmp/verify-l2-token.txt(600),脚本 6/6 通过。
- 只删当前行**不等于**作废:git 历史里的密钥必须靠轮换才能失效。
- 轮换前先核对过使用面:全仓库只有这一处,/root 下的脚本与运行进程环境里都没有。
- 生产库里临时建的留档表已 DROP,那行旧记录挪到
/root/gotmp/user-keys-rotation-backup-20260912.json(600)。
- 轮换过程本身也踩了一个小坑:sqlite3 CLI 里没有 gen_random_uuid()
(那是网关 Go 驱动注册的函数),插入必须显式给 key_id —— 而当时旧行已经删掉了,
于是出现了一小段「无可用 key」的空档(立刻补上并双向验证过)。
|
2026-09-12 23:58:10 +08:00 |
|
|
|
33e44074e7
|
fix(tooling): pi-lens 的项目防护其实一直没生效(JSON 里带注释 → 整份被忽略)
## 现场
跑 `pi --help` 时有一行不起眼的警告:
[pi-lens] ignoring invalid project config …/.pi-lens.json:
Expected double-quoted property name in JSON at position 60
`.pi-lens.json` 是 `d50c55d` 专门加的项目级防护(关掉 pi-lens 的格式化与
autofix),但它的说明写成了 `//` 注释 —— 读 pi-lens 的源码确认:
PROJECT_CONFIG_BASENAMES = ['.pi-lens.json', 'pi-lens.json']; // 没有 .jsonc
raw = JSON.parse(text); // 不去注释
// 解析失败 → 打一行警告,然后**整份忽略**
所以「已经关掉了」这个结论是**错的**,防护从落地那天起就没生效过。
而它要防的是两次已发生的真实损失:一次是 `19a3161` 把约 7000 行 biome 重排
扫进功能提交(无法审查、还掩盖了一处删行),一次是 prettier 改坏
`client/electron/index.html` 并打破 `test/theme.test.mjs` 的两条断言。
失败方式是静默的:进程照常跑,只有启动时一行警告 —— 而我正是在
`pi --help` 的输出里才看见它。
## 修
- `.pi-lens.json` 改为**严格 JSON**(只有 `$schema` / `format` / `autofix`;
`$comment` 也不行 —— pi-lens 校验未知键,会为它刷一条
「unknown key … ignored」的警告,而噪声会训练人忽略告警)。
- 理由移到 `docs/DEV-TOOLING.md`(含 pi-lens 的源码依据、两次事故、
以及「为什么不是把格式化器配成本仓库风格」——试过,仍改 17 个文件)。
- `biome.jsonc`(允许注释)里加指针:它只是两道闸中的一道,真正的总开关是
`.pi-lens.json`,而那份**必须是严格 JSON**。
- 验证:`pi --help` 现在 **0 行** pi-lens 输出;反向对照(故意塞回一行注释)
会重新出现 `ignoring invalid project config`。
## 顺带:仓库里有一个活的凭据
`scripts/verify-l2.sh` 把**管理员 user key 硬编码**在文件里(`1759666` 起就在
git 历史里)。实测它**仍然有效**(`/auth/me` → 200),属于用户 `jianf`。
- 已改为从 `VERIFY_L2_TOKEN` 或 `/root/gotmp/verify-l2-token.txt`(600)读取,
两者都没有时**报错退出**(反向对照验过:EXIT=2,不静默跑);
- 跑通一次确认可用(6 通过 0 失败)。
**但删掉当前这行不能把它从历史里拿掉** —— 要真正作废必须轮换那把 key。
这需要你定(它是你账号的密钥,可能还有别的工具在用),见提交后的说明。
|
2026-09-12 23:49:55 +08:00 |
|
|
|
1759666d38
|
chore: L2 验证脚本
|
2026-09-06 21:37:13 +08:00 |
|