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

5.0 KiB
Raw Permalink Blame History

待修复: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:54HTML 用 edit_file 加了 5 张 <img src="assets/*.svg">Agent 宣称「SVG 已 write_file 落盘」,但 public/assets/kids-posture-hero.svg5 个文件均不存在 → 页面 200、插图全部 404(与 HTML 假交付同类问题,范围扩展到非 .html 资源)

根因(非意图识别)

层级 行为 第一次失败时
意图路由 chat-intent-router / buildAutoChatSkillPrefix 命中「生成页面」
技能提示 static-page-publish 要求「必须先 write_file」 已注入(软约束)
模型执行 应调用 write_file 未调用
Finish materialize 仅从 messages 中 tool call 写盘 无 tool call
回复校验 拦截「已生成」但磁盘无文件 H5 无此闸门

微信通道已有 shouldRetryHtmlGenerationReplywechat-mp.mjs),检测到链接/宣称与磁盘不一致时会 session 重试。H5 SSE 路径(server.mjstkmindProxy.proxySessionEventsonAfterFinish没有等价逻辑

拟议修复(合并其它分支后统一做)

目标行为

当 Assistant 回复同时满足:

  1. 含「页面已生成 / 生成完成 / 已发布 / …」类成功宣称,MindSpace/.../public/*.html 公网链接
  2. 磁盘上对应 public/*.html 不存在(或仅为 stub

则:

  1. 自动重试:向同 session 发送 follow-up(参考 wechat-mp.mjsexecuteSessionReply),明确要求 load_skillstatic-page-publishwrite_file 写入缺失路径
  2. 继续检验:重试后再次 materializeMissingPublicHtmlWrites + 磁盘校验;仍失败则打 [MindSpace] warn 日志(可选:前端提示「页面落盘失败,请重试」)
  3. 资源完整性:解析已落盘 HTML 中的 src/href(含 assets/*.svg|png|jpg|webp),缺失则一并列入 retry 清单(materializePublicHtmlWritesFromSessionEvent 目前只处理 .html tool call,不能假设插图已落盘)
  4. 上限:建议最多 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.mjsshouldRetryHtmlGenerationReplycreatePublicHtmlLinkExistsresolveHtmlPublishArtifacts 等(考虑抽到共享模块,避免 H5 直接 import 整个 wechat-mp
单测 mindspace-h5-html-finish-guard.test.mjs;接入 npm run verify:mindspace-publish-guards
文档 合并后更新 mindspace-publish-and-chat-finish.md

与进行中分支的协调点

合并前必须先读并对齐:

不要在 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
  • 微信 retrywechat-mp.mjsshouldRetryHtmlGenerationReplyexecuteSessionReply
  • 落盘:mindspace-public-finish-sync.mjsmaterializeMissingPublicHtmlWritessyncPublicHtmlAfterFinish
  • 技能前缀:chat-skills.mjsgenerate-page prompt