Просмотр исходного кода

docs: 交接清单按合并后状态更新

- 当前分支改为 master(feat 分支已合并删除)
- 基线更新为 1522 条单测

Co-Authored-By: Claude Code <noreply@anthropic.com>
Huccct 1 час назад
Родитель
Сommit
2d6c1dd5fc
1 измененных файлов с 134 добавлено и 134 удалено
  1. 134 134
      docs/00-tracking/2026-11-10-交接-未完成事项清单.md

+ 134 - 134
docs/00-tracking/2026-11-10-交接-未完成事项清单.md

@@ -1,181 +1,186 @@
-# 交接文档:未完成事项清单(2026-11-10)
+# 交接文档:未完成事项清单
 
 | 字段 | 值 |
 |---|---|
-| 用途 | **新会话接手时先读这份**,了解做到哪了、还剩什么 |
+| 首次编写 | 2026-11-10 |
+| 最后更新 | **2026-11-11**(已合并 origin/master 与本功能分支,基线 `npm test` 1522 条) |
+| 用途 | **新会话接手时先读这份** |
 | 测试容器 | `agent-node-lj-test`(`@zgdcmgnqcd:fmode.cn`)— 用户裁决,**所有容器测试只用这一个** |
-| 当前分支 | `master`,所有改动**未提交**(等用户确认) |
-| 基线状态 | `npm test` 1459 条全过 · `tsc` ✅ · `npm run build` ✅ |
+| 当前分支 | **`master`(唯一长期分支)**;`feat/file-hub-session-isolation` 已合并并删除,后续一律在 master 上提交 |
+| 基线状态 | `npm test` 1459+ 条 · `tsc` ✅ · `npm run build` ✅ |
 
 ---
 
-## 一、本轮已完成(不用再做)
-
-| 项 | 说明 | 验证 |
-|---|---|---|
-| 计时稳定递增 | 从用户发消息起算,不再卡住跳变 | 单测 + 真机 38s |
-| 容器日志不再刷屏 | 工具行占比过半才折叠 | 单测 |
-| 「已完成」口径 | 无交付时显示「已结束 · 无交付」 | 单测 |
-| 超时改为 3 分钟 | `TURN_TIMEOUT_MS = 180_000` | 单测 |
-| 超时提示必弹 | 看门狗独立于会停的时钟 | 单测 |
-| 提示每条消息只弹一次 | 超时即终态,刷新不再弹 | **真机验证通过** |
-| 移动端提示独立样式 | 整宽、按钮 48px、不竖排 | 真机验证 |
-| 结果 emoji 合并 | `✅/❌` 连续行合并成一段,不再一卡一行 | 单测 |
-| PC/Mobile 自动切换 | 按宽度自动跳转,去掉手动按钮 | **真机验证通过** |
-| 成果文件名与存储键分离 | `prettifyStorageKey` | 单测 |
+## 一、已完成(不用再做)
 
----
-
-## 二、未完成的环节(新会话要做的)
+### 1.1 会话体验
 
-### P0 · 阻断体验
-
-#### 1. 上云链路只在「临时房间」验证通过,**真实会话仍有缺陷**
+| 项 | 说明 |
+|---|---|
+| 计时稳定递增 | 从用户发消息起算,不再卡住跳变 |
+| 容器日志不刷屏 | 工具行占比过半才折叠 |
+| 「已完成」口径 | 无交付显示「已结束 · 无交付」 |
+| 结果 emoji 合并 | 连续 `✅/❌` 行合并成一段 |
+| 超时改 3 分钟 | `TURN_TIMEOUT_MS = 180_000` |
+| 超时提示必弹 | 看门狗独立于会停的时钟 |
+| **提示每条消息只弹一次** | 超时即终态,刷新不再弹(真机验证) |
+| 移动端提示独立样式 | 整宽、按钮 48px、不竖排 |
+| **PC/Mobile 自动切换** | 按宽度自动跳转,去掉手动按钮(真机验证) |
+
+### 1.2 文件与成果(提交 `08b3d58`)
+
+| 项 | 说明 |
+|---|---|
+| **新建云函数 `fmode-file-hub`(`THXAOGKiPK`)** | registerFile / listBySession / listByAgent;老函数**一行未动** |
+| **成果按会话隔离** | 新增 `threadRoot` 字段;同一 room 不同 thread = 不同会话 |
+| **用户上传改为「先云盘、再 Matrix」** | 云盘失败则不发消息,不再半成功 |
+| **群聊此前完全不上云** | 已修 |
+| **群聊成果串台** | 已修 bug |
+| **右侧栏改为从数据库加载** | 不再靠消息正文找链接 |
+| 交付协议解析改用容器真实格式 | 老的 `<!--fmode:files-->` 已删除(容器从未实现) |
+| 上云失败可观测 | UI 如实展示 |
+| 上传大小限制 | `upload-limits.ts`(20MB,目前仅 App 侧) |
+
+### 1.3 容器侧接入(已完成并独立核验)
+
+- `fmode-deliverable-hook.py` 新增 `_thread_root()`:`HERMES_SESSION_ID` 查 `state.db.sessions.thread_id`,**105/105 命中**
+- 登记切到新函数 `registerFile`;取不到 threadRoot 时 **SKIP 不造假值**
+- 老链路(`report`/`enqueueArtifact`/`drain`)原样并存
+- **hook 每次调用 exec 脚本 → 改完即生效,无需重启 gateway**
+
+**核验证据**(不只信自报):
+```
+listBySession threadRoot=$approot_mw9h5oi6_m96imj7x:fmode.cn
+count = 2
+  4pA5vi4s4q  hub-verify.md                uploadedBy=agent  ← 容器产出
+  HmeAvoDKkv  m-composer-1794298339227.md  uploadedBy=user   ← 用户上传
+```
 
-**已确认的事实:**
+---
 
-- 我用脚本建临时房间测试,连续 3 轮 **6/6 全绿**(上传、成果登记、正文带链接、无本地路径)
-- 但在 **App 真实会话**里发需求(`app-e2e-1794288808946.md`):
-  - ✅ 文件**确实上云了**(成果表有记录)
-  - ❌ 回复正文**没有 https**
-  - ❌ 正文残留「本地路径:(本地工作目录)」
+## 二、未完成的环节
 
-**待查:**
+### P0 · 阻断体验
 
-- 临时房间 vs 真实会话(thread)在 hook/插件层面的差异
-- 容器自述「插件需重启才生效」,但那之后真实会话仍未验证通过
+#### 1. 「容器产出文件 → 回复带链接」在**真实回合**尚未完整验证
 
-**涉及文件:** 容器侧 `fmode-deliverable-hook.py`、`plugin-deliverable-inject`
+**限制(如实披露)**:
 
-#### 2. 容器端「默认上云」尚未在真实场景证明
+> 当前容器的 DM 房间历史上 **0 条**有 thread 的 session(`thread_id` 全是 NULL)。
+> 因此「容器产出文件的真实回合」尚未取得完整证据。
 
-**用户核心需求**:用户**不提**「上传」二字,产出文件也要自动上云。
+- 容器已用同一 threadRoot 实跑登记并回读成功(`4pAvi4s4q`)→ **登记逻辑可用**
+- 但「用户发需求 → 容器产出 → 自动登记到该会话 → 回复带 https」这条**端到端链路**还没在真实会话里跑通一次
 
-**当前状态:**
+**下一步:**
+1. 在 App 里**新建会话**(有 thread 根),发一条不带上传字眼的需求
+2. 检查三件事:文件是否登记到**该会话**、回复正文是否带 https、右栏是否显示
 
-- 机制已建(`config.yaml` hooks + Python 插件)
-- 临时房间通过,真实会话未通过
-- 容器曾自编假链接(`https://s3.fmode.cn/user/xxx/report/xxx.md` → 404),已修三层防护,但需重新验证
+#### 2. 容器回复正文仍会带 `/opt/data/work/...` 本地路径
 
-#### 3. 云函数无删除能力,测试数据清不掉
+已知未解决,属容器任务书范围。App 侧**不伪造处理**(用户明确要求不要兜底)。
 
-**问题:**
+#### 3. 云函数没有删除能力,测试数据清不掉
 
-- 云函数 `lfYlgU7SkK` **没有 `remove` action**
-- 成果表里堆积了测试数据:`meeting-*`、`app-e2e-*`、`retro-*`、`SKILL.md`、`probe-*`
-- 之前建的临时 Matrix 房间也在
+- `lfYlgU7SkK` **没有 `remove` action**
+- 成果表堆积:`meeting-*`、`app-e2e-*`、`retro-*`、`SKILL.md`、`probe-*`
+- 临时 Matrix 房间也在
 
-**需要:** 云函数加删除能力,或提供控制台清理方式
+**需要**:`fmode-file-hub` 加删除能力,或提供控制台清理方式
 
 ---
 
 ### P1 · 应该做
 
-#### 4. 用户上传文件未改成「先上云」
+#### 4. 老成果数据没有 `threadRoot`
 
-**用户要求**:「我上传文件之后,默认上传到我的云盘里面,然后同步到数据库」
+按用户裁决**不处理** —— 老数据不在会话右栏显示。
 
-**当前实现**(`chat.service.ts` `uploadPending`):
-```
-先发 Matrix 附件(mxc://)→ 再额外传 S3
-```
-
-**容器给出的硬约束**(读源码确认):
-```python
-# adapter.py:2136
-if url and not str(url).startswith("mxc://"):
-    return   # https 放进媒体事件会被直接丢弃
-```
-
-**结论:**
+#### 5. 上传大小限制只在 App 侧
 
-| 文件大小 | 通道 |
-|---|---|
-| ≤20MB | 必须走 `mxc://`(容器只认这个) |
-| 大文件 | 先上云 → **纯文本 body** 放 https |
-
-**待做:** App 侧按大小分流,大文件走「云端 + 正文链接」
-
-#### 5. 本地文件不累积,但做不到「零本地写入」
+`AlP56LCKFm` 本身**没有 size 校验**,20MB 限制目前只在客户端。
+服务端硬门禁需改老函数(用户要求不动老函数 → 未排期)。
 
-**用户诉求**:本地空间有限,文件主要在云端。
+#### 6. 本地文件做不到零写入
 
-**容器侧已做**:PUT 成功后 `md5` 校验通过即删除本地源文件。
+服务端契约要求 `report`/`registerFile` 收**容器本地路径**,drain 要 `open(路径)` 才能 PUT。
+容器已做「PUT 成功后 md5 校验再删」,但秒级中转不可避免。
 
-**限制**:服务端契约要求 `report`/`enqueueArtifact` 收**容器本地路径**,drain 要 `open(路径)` 才能 PUT —— 秒级中转不可避免。
-
-**若要真正零写入**:需改云函数加 base64/流直传 action(平台侧改动,未排期)
+**真要零写入**:需云函数加 base64/流直传 action(平台侧改动)。
 
 ---
 
 ### P2 · 技术债
 
-#### 6. `transform_llm_output` hook 仍是坏的
-
-容器读源码确认两个断点:
-- `shell_hooks._payload_fields()` 把正文塞进 `extra`,但 hook 读顶层
-- hook 输出 `{response_text}`,但 Hermes 只认 `{context}`
+#### 7. `transform_llm_output` shell hook 仍是坏的
 
-**当前绕法**:改用 Python 插件 `plugin-deliverable-inject`(回调返回 str 才被采纳)
+- `shell_hooks._payload_fields()` 把正文塞进 `extra`,hook 读顶层
+- hook 输出 `{response_text}`,Hermes 只认 `{context}`
 
-**风险**:若 Hermes 升级改动契约,插件会失效,需重新验证
+**当前绕法**:Python 插件 `plugin-deliverable-inject`(回调返回 str 才被采纳)。
+**风险**:Hermes 升级改动契约会失效。
 
-#### 7. 交付协议 v2(`<!--fmode:files-->`)从未被容器实现
+#### 8. 任务域 `fetchTasks`
 
-- App 侧已实现(`core/declared-files.ts`)
-- 容器明确回答「不知道这个协议,从没写过」
-- **实际交付走的是 `m.file` + 正文链接**
+仍为桩,云函数接收端未实现。用户曾裁决挂起。
 
-**建议**:要么让容器实现,要么在 App 侧废弃该分支
+#### 9. `messageCount` 双写虚高
 
-#### 8. `messageCount` 双写虚高
-
-左栏消息数偏大,未修。属数据模型变更。
+左栏消息数偏大,未修。
 
 ---
 
 ## 三、需要从容器获得的信息
 
-### 已问到(有答案)
+### 已问到(有答案,不用再问)
 
 | 问题 | 容器回答 |
 |---|---|
-| 回复分几条发? | 分条发,回执先发、结果后发、长任务插播进度 |
+| 回复分几条发? | 分条:回执先发、结果后发、长任务插播进度 |
 | 挂 thread 吗? | **不挂**,只发普通消息 |
 | `tool_progress` / `interim` | `all` / `true` |
 | 上传链路 | `report` → `enqueueArtifact` → `drain` → md5 回读 |
-| `report` 会自动入队吗? | **不会**,必须显式 `enqueueArtifact` |
-| 用哪个云函数 | `lfYlgU7SkK`(**不是** `xlosi3m46q`,那个对任何 action 都返回 200) |
+| `report` 自动入队? | **不会**,必须显式 `enqueueArtifact` |
+| 用哪个云函数 | 老的 `lfYlgU7SkK`;新登记用 `THXAOGKiPK`(**不是** `xlosi3m46q`,它对任何 action 都返回 200) |
+| threadRoot 怎么取 | `HERMES_SESSION_ID` 查 `state.db`,105/105 命中 |
 
 ### 还需要问
 
-1. **真实会话 vs 临时房间的差异**:为什么临时房间能带链接、真实会话不能?
-2. **插件重启后是否真的加载**:需要进程启动时间 vs 插件 mtime 的证据
-3. **产出文件的判定规则原文**:当前 `SYSTEM_PREFIX_DENY` 列表能否覆盖所有系统文件
-4. **大文件读取能力**:https 纯文本链接的最大可靠大小
+1. **真实会话里触发一次产出**:确认「产出 → 登记 → 回复带 https」整链
+2. **`/opt/data/` 路径残留**什么时候能彻底不出现
+3. **大文件读取能力**:https 纯文本链接的最大可靠大小
 
 ---
 
-## 四、已知的关键流程(新会话必读)
+## 四、已知的关键流程
 
-### 4.1 容器上云链路
+### 4.1 容器上云链路(当前)
 
 ```
 ① 容器产出文件 → /opt/data/work/xxx
 ② post_tool_call hook → 写入队列状态文件(无网络 IO)
-③ transform_llm_output hook → report + enqueueArtifact + drain + md5 回读
-   ⚠️ 这一步的文本输出会被 Hermes 丢弃(hook 接口不兼容)
+③ transform_llm_output hook → 上传 + 登记(registerFile 带 threadRoot)
+   ⚠️ 这一步的文本输出会被 Hermes 丢弃
 ④ plugin-deliverable-inject(Python 插件)→ 把 https 追加进回复正文
-   ✅ 只有 Python 插件回调返回 str 才被采纳
-⑤ 上传成功后删除本地源文件
+⑤ 上传成功后 md5 校验通过 → 删除本地源文件
 ```
 
-**验证命令:**
+**验收命令:**
 ```bash
-npm run verify:upload              # 5-6 项判定,可指定 --agent / --prompt
+npm run verify:upload                        # 5-6 项判定
+node scripts/verify-file-hub.mjs             # 12 项
+node scripts/verify-real-upload.mjs          # 7 项(真实账号)
+```
+
+### 4.2 用户上传(已改为先云盘)
+
+```
+选文件 → 先传云盘 → 失败则**不发消息**(不再半成功)
+       → 成功后发 Matrix 附件
+       → registerFile 登记 uploadedBy=user
 ```
 
-### 4.2 Matrix 消息归属(App 侧)
+### 4.3 Matrix 消息归属(App 侧)
 
 `chat.service.ts` `belongsToSession()`:
 
@@ -184,10 +189,10 @@ npm run verify:upload              # 5-6 项判定,可指定 --agent / --promp
 | 有 thread 且 threadId === 会话 | 认领 |
 | 无 thread + dispatch 标记 | 丢弃(群聊派活) |
 | 无 thread + sessionId 标记 | 按 sessionId 比对 |
-| **无 thread + 实时 + 本会话有待回复消息** | **认领**(2026-11-10 新增,修「完全没反应」) |
+| 无 thread + 实时 + 本会话有待回复消息 | 认领(2026-11-10 新增) |
 | 无 thread + 历史回填 | 丢弃 |
 
-### 4.3 超时逻辑
+### 4.4 超时逻辑
 
 ```
 用户发消息 → 看门狗启动(每 5 秒)
@@ -197,40 +202,35 @@ npm run verify:upload              # 5-6 项判定,可指定 --agent / --promp
 下一轮重新开始
 ```
 
-**关键**:看门狗存活窗口 = `TURN_TIMEOUT_MS + 10s`,刷新后老消息超出窗口 → 不启动 → 不弹。
+看门狗存活窗口 = `TURN_TIMEOUT_MS + 10s`,刷新后老消息超出窗口 → 不启动 → 不弹。
 
----
-
-## 五、当前工作区状态
+### 4.5 端自动切换
 
-**未提交的改动**(`git status`):
+```
+按 window.innerWidth 判定:< 768 → mobile,否则 desktop
+  · 宽度够但 UA 是手机(横屏)→ 仍 mobile
+  · 窗口 resize → 防抖 400ms → 按目标端导航
+🔴 判据用「目标端」,不用 recheck().changed(否则直接窄窗口打开会卡住)
+```
 
-- `CLAUDE.md` — 加测试容器约定
-- `package.json` — 加 `verify:upload` 脚本
-- `src/app/` 下 21 个文件(会话、超时、设备切换、成果、合并等)
-- 新增文档 2 份 + 验收脚本
+---
 
-**新增的脚本**(`scripts/`):
-- `verify-upload.mjs` — 上云验收(可复用,任意容器)
-- `ask-ljtest.mjs` — 批量短问题问容器(长问题容器处理不了)
+## 五、关联文档
 
-> ⚠️ 大量 `debug-*.mjs`、`probe-*.mjs` 是临时调试脚本,交接前应清理。
+| 文档 | 用途 |
+|---|---|
+| `docs/00-tracking/2026-11-10-修改留痕-文件台账与按会话隔离.md` | **最新**:file-hub 云函数 + 容器接入 |
+| `docs/02-design/plans/2026-11-10-文件上云完整方案.md` | 上云方案设计 |
+| `docs/03-interface/容器执行-切换文件台账-20261110.md` | 容器侧任务书 |
+| `docs/03-interface/容器方案-如何可靠拿到threadRoot-20261110.md` | threadRoot 取法 |
+| `CLAUDE.md` | 项目规则(含测试容器约定) |
 
 ---
 
 ## 六、新会话建议的起步动作
 
-1. **先跑基线**:`npm test` + `npx tsc -p tsconfig.app.json --noEmit`
+1. **跑基线**:`npm test` + `npx tsc -p tsconfig.app.json --noEmit`
 2. **确认容器在线**:`node scripts/matrix-check.mjs agent-node-lj-test`
-3. **复现 P0-1**:在 App 真实会话里发一条不带上传字眼的需求,看回复是否带 https
-4. **按本文档 §四 的流程定位断点**
-
----
-
-## 七、关联文档
-
-| 文档 | 用途 |
-|---|---|
-| `docs/02-design/plans/2026-11-10-文件上云完整方案.md` | 上云方案完整设计 |
-| `docs/00-tracking/2026-11-10-修改留痕-会话区三缺陷与上云链路.md` | 本轮修改留痕 |
-| `CLAUDE.md` | 项目规则(含测试容器约定) |
+3. **验证 P0-1**:App 里**新建会话**发一条不带上传字眼的需求
+   → 看 `listBySession` 是否出现在该会话、右栏是否显示、正文是否带 https
+4. 若不通,按 §四 的流程逐环定位