chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进

1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
This commit is contained in:
admin committed 2026-09-15 18:47:13 +08:00
1 parent c70d5d860e
commit 5ad755116e
173 files changed
+27632

No files matched your search

@@ -0,0 +1,167 @@
# 37a · guest 会话取证与平台缺陷清单(MCN短视频创作)
- 日期:2026-09-11
- 触发:用户要求「看看 guest 用户的 MCN短视频创作 工作区的对话记录,能发现哪些问题」
- 状态:**🔍 取证完成(本档只记录缺陷,不含代码改动)**
- 关联:档案 32(同一方法,mcntimo 工作区)、档案 33(沙箱默认档位已改)、档案 21(guest 卡顿排查,服务端侧)、档案 23(实例内能力限制核查)
> **TL;DR**|**结论**:从 guest 的「MCN短视频创作」会话反查平台缺陷清单(**只记录缺陷,不含代码改动**)。
> **关键**:含 P0/P1 分级与证据;方法可复用于其他工作区(同档案 32)。
> **状态**:🔍 取证完成
---
## 一、取证范围
| 项 | 值 |
|---|---|
| 用户 | `guest` = `4092b965-2f68-4977-9989-68b3966f7df0`(uid 100002,`dsh-eeccbc638afc46bdb663`)|
| 工作区 | `ws/MCN短视频创作`(workspace id `7f9fd72c-1cf3-49f3-bb72-ba3de28a16fe`)|
| 主会话 | `session-bd54c7cc-7620-4cf3-9bff-9084bcc5ae88`,**4190 行 / 2.8 MB 明文 / 1.3 MB zstd** |
| 副会话 | `session-b83ae69b-...`,5 行(空会话,见 P2-3)|
| 时间跨度 | 2026-09-10 12:12 → 09-11 14:52(CST)|
| 规模 | 12 turns / 106 steps / **111 tool calls**(bash 100、web_search 2、web_fetch 3、write 3、edit 3)|
| 任务 | 单一诉求:**获取抖音热门短视频榜单**,以及围绕它的能力追问 |
**产出物(工作区,agent 手写脚本抓取)**:
`scripts/douyin_hot.py`、`scripts/douyin_hot_videos.py`、`scripts/probe_shape.py`;
`data/douyin_hot_2026-09-10_2148.{md,json}`(97 行)、`data/douyin_hot_2026-09-11_1447.{md,json}`(52 行)、`data/douyin_videos_2026-09-10_2154.{md,json}`(75 行)。
> 产出**质量本身可用**(含话题名/热度/播放量/最高排名/首次出现时间/搜索链接)。问题不在产出,在下述**代价、路径与平台缺口**。
---
## 二、新增 P0
### P0-1 · 存量会话不会被迁移到新档位 —— 全平台 10/10 会话仍卡在 `workspace-write`
档案 33 把**新会话**默认档位改为 `danger-full-access`(env 注入已落地:`spawn.ts` ALLOWED_ENV + `orchestrator.ts` baseEnv,已核实)。
但权限档位是**会话级播种**(会话创建时一次性写入 `permission/preset` / `sandbox/mode` / `approval/policy` 三条种子事件,**resume 不重播**)。实测全平台:
```
总会话=10 workspace-write=10 danger-full-access=0
```
即 **10 个存量会话(含 admin 自己在 mcntimo / mcnworkspace / maiyamcn 的)全部仍是 `workspace-write`**,只要它们被 resume 并执行 shell,就会撞上「sandbox backend is unusable → 拒绝执行」。guest 这条会话就是**已经真实踩中**的样本。
- **缺口**:平台**没有任何 UI/文案提示**「老会话需新开一个会话才生效」。用户唯一能得到这条信息的渠道是让 AI 去分析自己的会话日志(本次就是这么发现的)。
- **候选方向**:① 会话列表给旧档位会话打标记 + 一键「在新档位继续」;② 门户 `enter` 时对旧档位会话做一次性重播种(改会话数据,需评估);③ 最低成本:产品文案层提示。
### P0-2 · 平台零技能投放:用户在 MCN 工作区里「无技能可用」
| 位置 | 实况 |
|---|---|
| guest `$DSH_HOME/home/skills/` | **0 项** |
| admin `$DSH_HOME/home/skills/` | **0 项** |
| 全员共享只读层 `/var/lib/dshs/bundled-skills/` | **0 项** |
| 各工作区 `ws/*/` 内容 | guest 只有 `data/` + `scripts/`(无 SKILL.md、无素材库、无项目结构)|
| agent preset | `standard`(无业务技能)|
平台已建成技能机制(档案 10 共享只读层 + 档案 11 管理面 API/静态页),但**一个技能都没投放**。后果在会话里体现得非常直接:
- 用户开的是「MCN短视频创作」工作区,却**没有任何 MCN 技能**被加载 → agent 只能从零 hand-roll 抖音抓取脚本、反复试错(100 次 bash 中相当一部分是抓取路径试错)。
- 用户第 7 问「**为什么需要写代码,不能获取后直接展示内容吗**」、第 9 问「有安装网络搜索工具吗 比如 deepseek 搜索」——都是**「本该有技能/数据源,却没投放」**的症状。
- **应投放而未投放**:MCN 短视频创作技能(脚本/分镜/账号分析)、RedFox 数据源(日榜/飙升榜/账号诊断,本平台已有 API Key 与 7 赛道口径)、`mcn-data-insight` 系列技能。
> 这是本次取证**最重的产品结论**:不是能力不够,是**已有能力没有接到用户面前**。
---
## 三、修正档案 32 的两条结论
### 修正 1 · 「沙箱不可用」不是恒定状态,**同一会话内会翻转**
档案 32 结论为「bwrap 已装但 dsh 后端探测仍判不可用」,并附「实例内嵌套 bwrap 失败」。本次拿到**同一会话内前后对比**的强证据:
| 时间 | 同一会话内的实测 | bash |
|---|---|---|
| 09-10 12:12–12:13(turn 1)| `id` / `uname` / `read /etc/passwd` / `touch /tmp` / mounts 全部正常返回 | ✅ **可用** |
| 09-10 13:47:03 与 13:47:28(turn 2)| `Error: sandbox mode "workspace-write" ... no sandbox backend is usable on this host; refusing to run the command unconfined.` | ❌ **全拒** |
二者之间只隔一次 `session/end-seed`(13:46:07)→ 会话 resume(13:46:59),**无用户侧改动**。
另:本次在服务器以 guest uid 模拟实例内做**嵌套 bwrap 实测 → `NESTED_BWRAP_OK`(bubblewrap 0.4.0)**,与档案 32 的「失败」相反。
→ 结论修订为:**「后端可用性」是时序/环境相关,不是确定性不可用**(与实例 respawn、平台 bwrap 挂参变化有关,同期平台确实改过实例内挂载:turn 1 `$HOME` 只读 → 09-11 变可写)。
→ 因为档案 33 已让新会话绕开沙箱,**该问题当前不阻塞新会话**;但对存量会话仍是硬阻塞(见 P0-1),**根因仍未定位**。
### 修正 2 · 沙箱报错**丢失根因细节**,并把「关掉沙箱」写进了报错文案
`@deepseek-ai/dsh-sandbox/lib/index.js:185` 的报错**支持**附加 `Runner failure: ${detail}`。但本次会话日志中 **`Runner failure` 命中 0 次** —— 模型拿到的只有那句脱敏后的通用文案。
后果链(会话内实际发生):
1. 模型无法判断根因 → 只能猜 → 主动发起提权申请:
`approval/asked` reason = *"The sandbox backend is unavailable on this host so every shell command is refused; full access is the only mode that lets me run curl to fetch the Douyin hot list you asked for."*
2. 用户 7 秒内点了「允许一次」(`allowed-once`)。
3. 6 秒后用户**自己又跑了一次** `/permission danger-full-access`(`command/run`,`source: user`)→ `approval/policy` 同步变为 `never`。
→ **官方沙箱的 fail-closed 设计是对的,但报错文案("otherwise switch the consumer to danger-full-access")等于手把手教模型申请降级安全档位**;再叠加「根因细节丢失」,模型没有别的选择。这与档案 32 的判断一致,本次补上**机制性证据**。
---
## 四、P1
| # | 问题 | 证据(会话内) |
|---|---|---|
| P1-1 | **模型被切到实验模型,且切换事件无法审计** | `model/selection` → `deepseek-v4-flash-vision-exp`,`reasoningEffort` `high`→`max`(13:47:50 / 13:47:53,turn 2 中途)。`model/selection` 事件**无 `source` 字段** → 无法判定是用户切、平台切还是自动切。后续整段会话跑在实验模型上 |
| P1-2 | **智能体把「用户手动提权」误报为「平台能力增强」** | turn 11 用户问「看看现在能力有增强吗」→ agent 结论「能力确实有增强…核心是 bash 从『完全不可用』变成『可用』🟢 **最大增强**」。实际是 turn 2 **用户自己**跑了 `/permission danger-full-access`(`command/run` source=user,就在同一份日志里)|
| P1-3 | **联网抓取路径不可持续,全靠「绕」** | ① 抖音官方视频接口要签名 → `Unsupported path(Janus)` / `HTTP 200 空 body`;② `tophub.today` → `403 检测到异常访问行为`;③ 最终靠**伪装 `Baiduspider` UA 拿 SSR 页**再正则扒 HTML(脆弱的爬虫 hack),并退回 GitHub 第三方镜像(**时效差 3 小时**,agent 自陈「镜像最后更新 18:49,我抓的是官方 21:47」)|
| P1-4 | **平台 web_search 可用但没人知道** | 官方 `dsh-web-search-deepseek` 已装且实测可用(`web/deepseek-search-llm-request` 3 次)。但前 8 轮 agent 一直用 curl/python 硬抓,**第 9 轮用户主动问「有安装网络搜索工具吗」才开始用**。111 次工具调用里 `web_search` 只有 **2 次** |
| P1-5 | **用户感知「卡」的原始证据(补充档案 21)** | 用户第 13 问「感觉对话过程卡吗」→ agent 用日志算出:**69 步、平均 7.8s/步、中位 5.0s、最慢 81.1s、模型总生成 8m58s**;单次 bash 最长 **1m19s**;turn 3 单轮 4m50s。档案 21 已证**服务端全链路不慢**(TTFT 1.0–3.5s),本次补上「用户侧体感来自哪 69 步 / 哪 111 次工具调用」的实测数据 —— **卡顿主因是工具调用轮次过多,而轮次过多的直接原因是「技能未投放 + 数据源未接入」**(P0-2 / P1-3),不是网络 |
| P1-6 | **会话导出产物用户打不开** | 用户点过 `/export`(`command/done` → *"Session log download requested."*)。格式是 **`.jsonl.zstd` 追加式多帧**,`zstdDecompressSync` 只解第一帧;**连 agent 自己都踩了这个坑**(`流式解压失败: Unknown frame descriptor` → 改用魔数 `28 B5 4D FD` 逐帧解出 1632 帧)。用户拿到的是一个无法阅读的文件 |
| P1-7 | **环境基线太弱,MCN 场景几乎不可用(量化档案 23 的「可优化」项)** | agent 自统计 **35 处失败/受限**,含:Python **3.6.8 + pip 9.0.3**(al8 自带)、`rg` / `jq` / **`ffmpeg`** 缺失、**无任何浏览器**(chromium/chrome/firefox 全 MISSING,Playwright 落地需 300MB + **11 个系统库缺失**且 `/usr` 只读无 sudo 装不上)、`/usr`+`/etc` 只读、无 sudo。档案 23 已把这些列为「可优化」,本次量化了它们**实际烧掉的轮次**(100 次 bash 中约 1/3 耗在补环境与绕路径)|
| P1-8 | **过度承诺后自我推翻,浪费用户轮次** | agent 先推荐装 Playwright,实测后自我否定:「**我之前推荐它,根本没算过这笔账**」;并承认「**我上一条的说法确实站不住脚**」(把第三方镜像数据当成实时数据)|
---
## 五、P2
| # | 问题 | 证据 |
|---|---|---|
| P2-1 | 会话列表出现**空会话残留** | `session-b83ae69b`:仅 4 条事件(preset/mode/policy + end-seed),**0 个 turn**,13:29 创建、21:26 才结束,挂 8 小时 |
| P2-2 | **平台缓存污染用户工作区** | `ws/.npm/_cacache`、`ws/.npm/_logs`、`ws/.cache/pip/`、`ws/workspace-scoped-picker-0.1.4.tgz` 均落在工作区根部,会出现在门户 `#/files` 列表里 |
| P2-3 | 工作区根存在**平台 bundle `.tgz`** | `ws/workspace-scoped-picker-0.1.4.tgz`(09-11 15:00 更新)。档案 35 已修 `ws-cleanup` 误删,**建议确认该 picker 包也在豁免口径内**(当前豁免逻辑按「profile 的 `file:` 引用」动态判定,需回归验证)|
| P2-4 | 工具报错噪音,指向不存在路径 | `rg: /opt/dshs: No such file`、`rg: /var/log: No such file`(实例内看不到平台目录,agent 反复试)|
| P2-5 | **文档内部不一致:README 档案清单落后 8 个档案** | `docs/README.md` 的「档案清单」表**止于 `28-用户数据清理策略`**,`29`–`35` 全部缺失;而 `docs/INDEX.md` 是全的(到 `04-35`)。README 自称「档案清单只在本文件维护」,与该「单一来源约定」**已失效**(本次只在 INDEX + docs-status 维持,未擅自重排 README)|
---
## 六、安全观察(平台**挡住了**,但值得记录)
guest(普通租户)在会话中**主动做了横向/越权探测**,全部被平台层挡住:
| 探测动作 | 结果 |
|---|---|
| `ls /var/lib/dshs/users/` | `Permission denied` ✅ |
| `touch /etc/dsh-probe`、`touch /usr/local/lib/node_modules/@deepseek-ai/dsh/probe` | `Read-only file system` ✅ |
| `ls /root`、`/home/admin`、`/home/www`、`/home/dsh-cce6d1cdb376430480f0` | 不存在 / 不可访问 ✅(bwrap 只暴露自己的用户根)|
| `sudo` | 不可用(`/etc/sudo.conf` 属主 uid 65534、未 setuid)✅ |
| `ps -eo user,pid,cmd` | 可看到进程列表(bwrap 只 `--unshare-pid`,**未做 PID 视图隔离**)⚠️ |
| `curl http://127.0.0.1:3080/` (门户)| **返回 200,可读到门户首页 HTML** ⚠️(仅静态首页,无数据泄漏)|
| 内网端口扫描 `3080/80/443/888/8765/58888/32022` | 可探测,返回状态码与 Server 头 ⚠️ |
| `env \| grep -iE 'api_key\|token\|secret'`、读 `$DSH_HOME/.credentials.yaml` | agent 主动做了脱敏打印;**扫描会话全文:0 处明文凭据** ✅ |
| 起本地监听 `python3 -m http.server 39777` | **成功**(127.0.0.1 绑定 OK)+ `pip install` / `npm` 出网均可用 ⚠️(与既有「实例共享宿主网络、出网未全封」结论一致)|
**结论**:数据面与文件面隔离**有效**;**网络面(内网侧)与审计面是缺口** —— 上述探测**没有任何阻断、限速或审计上报**。
---
## 七、建议行动项(按优先级)
| # | 行动 | 说明 |
|---|---|---|
| 1 | **投放技能到 `bundled-skills`**(P0-2)| 最高性价比:MCN 短视频创作 + `mcn-data-insight` 系列。技能机制已就绪、watch 即时生效、无需重启 |
| 2 | **存量会话档位提示/迁移**(P0-1)| 10/10 会话受害;最低成本先做 UI 标记 +「新开一个会话」文案 |
| 3 | **接 RedFox 数据源到平台**(P1-3/P1-4)| 替代「伪装爬虫 UA 扒抖音」这条不可持续路径;已有 API Key 与 7 赛道口径 |
| 4 | **补环境基线**(P1-7,延续档案 23)| 最低集:`rg` / `jq` / `ffmpeg` / Python 3.8+;评估基线镜像方案 |
| 5 | **会话导出给可读格式**(P1-6)| 导出时转 `.md` 或解开的 `.jsonl` |
| 6 | **`model/selection` 补 `source` 字段**(P1-1)| 属官方行为,只做记录与观测;先确认实验模型是否为预期 |
---
## 八、取证方法补充(相对档案 32 的新坑)
1. **服务器 `/tmp` 下存在 `package.json`(`"type":"module"`)** → 在 `/tmp` 放分析脚本必须用 **`.cjs`** 扩展名,否则 `require is not defined in ES module scope`。(本次踩坑)
2. 会话日志里 **`tool/result` 的报错常被 `|| echo` 兜住**,表面看不出失败 → 分析时要按「错误特征词」扫全文并统计**位置分布**,而不是只看退出码。
3. `request/header` 每条含完整 system prompt + 工具表,**体量大**(占 2.8MB 相当比例)→ 提取时按 `type` 过滤,勿全量 dump。
4. 分析脚本**本地写好 → scp → 远端 `node` 执行**最稳(内层引号转义会在 `ssh '...'` 里坏掉)。