Files
memind/docs/pending-fixes/h5-public-html-fake-delivery-guard-20260706.md
john 08feae8bef feat(h5-session): Session Broker、run SSE replay 与 Finish 竞态修复
落地 H5 Session 架构 Patch 1–5(Broker 收口、Router decision、SSE taxonomy、goosed 边界检查),
并新增可选 MEMIND_RUN_STREAM_REPLAY run 事件回放与 H5 假交付 guard;修复 Finish 先于 agent-run
gate 导致 UI 永久 loading 的竞态,接入 verify:h5-session-patches 回归脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 14:19:48 +08:00

82 lines
5.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 待修复:H5 页面「假交付」检测与 write_file 自动重试
> **状态:已实现 guard 模块(2026-07-06),默认关闭 `MEMIND_H5_HTML_FINISH_GUARD=0`**
> **原暂缓原因**H5 session / broker 分支已在本分支落地,Finish guard 已接入 `server.mjs` onAfterFinish。
## 症状
- H5 Agent 回复含「页面已生成 / 生成完成 / 已保存至 public/…」及公网 Markdown 链接
- 用户点击链接 **404**
- 磁盘 `MindSpace/<userId>/public/*.html` **不存在**
- Finish 后 `syncPublicHtmlAfterFinish` 无 tool call 可 materialize,文件不会凭空出现
**典型案例(103 生产,2026-07-06):**
- 目标:`public/kids-posture-business.html`
- 第一次:Agent 在聊天里输出完整商业包内容 + 链接,但未调用 `write_file` → 404
- 用户追问后第二次同会话重试:真正 `write_file` 落盘 → 200
- **后续(同页 10:54**HTML 用 `edit_file` 加了 5 张 `<img src="assets/*.svg">`Agent 宣称「SVG 已 write_file 落盘」,但 `public/assets/kids-posture-hero.svg`**5 个文件均不存在** → 页面 200、插图全部 404(与 HTML 假交付同类问题,范围扩展到非 `.html` 资源)
## 根因(非意图识别)
| 层级 | 行为 | 第一次失败时 |
|------|------|--------------|
| 意图路由 | `chat-intent-router` / `buildAutoChatSkillPrefix` 命中「生成页面」 | ✅ |
| 技能提示 | `static-page-publish` 要求「必须先 write_file」 | ✅ 已注入(软约束) |
| 模型执行 | 应调用 `write_file` | ❌ 未调用 |
| Finish materialize | 仅从 messages 中 tool call 写盘 | ❌ 无 tool call |
| 回复校验 | 拦截「已生成」但磁盘无文件 | ❌ **H5 无此闸门** |
微信通道已有 `shouldRetryHtmlGenerationReply``wechat-mp.mjs`),检测到链接/宣称与磁盘不一致时会 **session 重试**。H5 SSE 路径(`server.mjs``tkmindProxy.proxySessionEvents``onAfterFinish`**没有等价逻辑**。
## 拟议修复(合并其它分支后统一做)
### 目标行为
当 Assistant 回复同时满足:
1. 含「页面已生成 / 生成完成 / 已发布 / …」类成功宣称,**或**含 `MindSpace/.../public/*.html` 公网链接
2. 磁盘上对应 `public/*.html` **不存在**(或仅为 stub
则:
1. **自动重试**:向同 session 发送 follow-up(参考 `wechat-mp.mjs``executeSessionReply`),明确要求 `load_skill``static-page-publish``write_file` 写入缺失路径
2. **继续检验**:重试后再次 `materializeMissingPublicHtmlWrites` + 磁盘校验;仍失败则打 `[MindSpace]` warn 日志(可选:前端提示「页面落盘失败,请重试」)
3. **资源完整性**:解析已落盘 HTML 中的 `src`/`href`(含 `assets/*.svg|png|jpg|webp`),缺失则一并列入 retry 清单(`materializePublicHtmlWritesFromSessionEvent` 目前只处理 `.html` tool call,不能假设插图已落盘)
3. **上限**:建议最多 1~2 次重试,避免无限 loop
### 建议落点(实现时再对齐当时主线)
| 模块 | 改动 |
|------|------|
| 新建 `mindspace-h5-html-finish-guard.mjs`(或并入 `mindspace-public-finish-sync.mjs` | 检测假交付、提取缺失 `public/*.html`、构建 retry prompt |
| `server.mjs` `onAfterFinish` | Finish 后 sync 仍失败 → 调用 guard 重试 → 再 sync + register artifacts |
| 复用 | `wechat-mp.mjs``shouldRetryHtmlGenerationReply``createPublicHtmlLinkExists``resolveHtmlPublishArtifacts` 等(考虑抽到共享模块,避免 H5 直接 import 整个 wechat-mp |
| 单测 | `mindspace-h5-html-finish-guard.test.mjs`;接入 `npm run verify:mindspace-publish-guards` |
| 文档 | 合并后更新 [mindspace-publish-and-chat-finish.md](../regression-guards/mindspace-publish-and-chat-finish.md) |
### 与进行中分支的协调点
合并前必须先读并对齐:
- [docs/h5-session-architecture-20260706.md](../h5-session-architecture-20260706.md)
- `session-broker.mjs``agent-run-gateway.mjs``tkmind-proxy.mjs` 的 Finish / SSE 收口
**不要在 broker 分支未合并前单独改上述文件的 Finish 路径**,否则合并冲突与行为分叉。
## 实现 checklist(待开工时)
- [ ] 其它分支(H5 session / broker)已合并 `main` 且 CI 绿
- [ ]`wechat-mp.mjs` 抽取或复用 HTML 发布校验纯函数
- [ ] H5 `onAfterFinish` 接入 guard + 有限次 retry
- [ ] 单测覆盖:假链接 404、retry 后落盘、正常 Finish 不误触发
- [ ] `npm run verify:mindspace-publish-guards` 全绿
- [ ] 103 手工:生成页 → 故意 mock 无 write_file → 确认自动 repair 或明确失败提示
## 参考
- 回归守卫:[mindspace-publish-and-chat-finish.md](../regression-guards/mindspace-publish-and-chat-finish.md)
- 微信 retry`wechat-mp.mjs``shouldRetryHtmlGenerationReply``executeSessionReply`
- 落盘:`mindspace-public-finish-sync.mjs``materializeMissingPublicHtmlWrites``syncPublicHtmlAfterFinish`
- 技能前缀:`chat-skills.mjs``generate-page` prompt