Add 103 H5 rich page rollout runbook
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
# 103 H5 页面生成灰度与发布复盘(2026-07-07)
|
||||
|
||||
> 适用场景:H5 / 服务号聊天中,长内容、结构化内容、适合读者阅读或转发的内容没有自动生成页面,或 103 开关/发布后需要做受控验证。
|
||||
|
||||
## 结论摘要
|
||||
|
||||
本次问题不是 `static-page-publish` 或 `sandbox-fs` 完全不可用,而是三层问题叠加:
|
||||
|
||||
1. 103 起初未开启 H5 session 灰度开关,生产仍停在旧路径。
|
||||
2. Router 将“长新闻 + 请整理给服务号用户阅读”判成 `direct_chat`,导致只回复摘要,不进入页面生成链路。
|
||||
3. Portal runtime 发布后,旧 `goosed-prod-*` 容器仍握着旧 live 目录 inode 的 bind mount,导致容器内写入成功但 host 当前 `/Users/john/Project/Memind/MindSpace` 看不到。
|
||||
|
||||
最终上线版本:
|
||||
|
||||
- Portal release: `20260707-154245-ea4c6b7`
|
||||
- Git commit: `ea4c6b707c2a95c5c63eed4ed5dc02f8a6f786bc`
|
||||
- Branch used for release: `release/rich-page-router-0707`
|
||||
- Production host: `john@58.38.22.103`
|
||||
|
||||
## 安全边界
|
||||
|
||||
- 不从脏工作区发包。
|
||||
- 不从功能分支、局部文件或单 commit 手工拷贝到 103。
|
||||
- 不直接 `rsync` 到 103/105。
|
||||
- Portal 发包必须走 `bash scripts/release-portal-runtime-prod.sh --yes`。
|
||||
- `.env` 灰度开关可以作为运行配置变更,但必须先备份、记录、重启、健康检查。
|
||||
- goosed 容器重建是运行态 remount 修复,不是源码变更;执行前后必须检查 18006-18014 健康。
|
||||
|
||||
## 本次启用的 103 H5 灰度开关
|
||||
|
||||
103 `.env` 当前启用:
|
||||
|
||||
```bash
|
||||
MEMIND_SESSION_BROKER_ENABLED=1
|
||||
MEMIND_ROUTER_NORMALIZED_DECISION=1
|
||||
MEMIND_SSE_EVENT_TAXONOMY=1
|
||||
MEMIND_RUN_STREAM_REPLAY=1
|
||||
MEMIND_H5_HTML_FINISH_GUARD=1
|
||||
MEMIND_SESSION_STREAM_REPLAY=1
|
||||
MEMIND_SESSION_BROKER_METRICS=1
|
||||
```
|
||||
|
||||
启用前备份:
|
||||
|
||||
```bash
|
||||
/Users/john/Project/Memind/.env.backup-h5-session-flags-20260707-151846
|
||||
```
|
||||
|
||||
启动日志应看到:
|
||||
|
||||
```text
|
||||
[Portal] Session Broker enabled (MEMIND_SESSION_BROKER_ENABLED=1)
|
||||
[Portal] H5 session flags: MEMIND_SESSION_BROKER_ENABLED, ...
|
||||
```
|
||||
|
||||
## 标准验证流程
|
||||
|
||||
### 1. 基础健康
|
||||
|
||||
103 本机:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'curl -fsS http://127.0.0.1:8081/api/status'
|
||||
```
|
||||
|
||||
外部入口:
|
||||
|
||||
```bash
|
||||
curl -ksS https://m.tkmind.cn/api/status
|
||||
```
|
||||
|
||||
Manifest:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 'cat /Users/john/Project/Memind/.release-manifest.txt'
|
||||
```
|
||||
|
||||
### 2. 账号级 smoke
|
||||
|
||||
使用生产账号登录后,创建 session 并发一条短消息。期望:
|
||||
|
||||
- `/api/agent/runs` 返回 `202`
|
||||
- run 最终 `succeeded`
|
||||
- 会话里 `message_count=2`
|
||||
- 助手回复 `OK`
|
||||
|
||||
### 3. 页面链路 smoke
|
||||
|
||||
发明确页面生成请求。期望:
|
||||
|
||||
- run events 有 `intent_routed`
|
||||
- route 为 `agent_orchestration`
|
||||
- `suggestedSkill=static-page-publish`
|
||||
- goosed 原始 session 有 `load_skill` 和 `sandbox-fs__write_file`
|
||||
- 最终 Markdown 链接能 `HTTP 200`
|
||||
|
||||
示例验收 URL:
|
||||
|
||||
```text
|
||||
https://m.tkmind.cn/MindSpace/a6fb1e97-2b0f-447b-b138-4561d8e5c53e/public/world-news-20260707.html
|
||||
```
|
||||
|
||||
### 4. 长内容/服务号场景 smoke
|
||||
|
||||
复测用户原始形态:
|
||||
|
||||
```text
|
||||
以下是今天(2026年7月7日,星期二)国际上的主要热点新闻:
|
||||
...
|
||||
请整理给服务号用户阅读。
|
||||
```
|
||||
|
||||
期望 run events:
|
||||
|
||||
```json
|
||||
{
|
||||
"eventType": "intent_routed",
|
||||
"data": {
|
||||
"route": "agent_orchestration",
|
||||
"reason": "结构化长内容适合生成页面承载",
|
||||
"source": "rule",
|
||||
"suggestedSkill": "static-page-publish"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 本次代码调整
|
||||
|
||||
文件:
|
||||
|
||||
- `chat-intent-router.mjs`
|
||||
- `chat-intent-router.test.mjs`
|
||||
|
||||
策略:
|
||||
|
||||
- 保留短问答、普通摘要、故事/诗歌等 direct chat。
|
||||
- 对“服务号/公众号/H5/读者/用户/转发/分享/阅读”等场景中较长、结构化、多条目内容,优先走 `static-page-publish`。
|
||||
- 避免完全依赖 LLM router 判断,否则容易把“整理给服务号用户阅读”判为纯文字摘要。
|
||||
|
||||
本地验证:
|
||||
|
||||
```bash
|
||||
node --test chat-intent-router.test.mjs direct-chat-service.test.mjs chat-skills.test.mjs
|
||||
npm run verify:h5-session-patches
|
||||
```
|
||||
|
||||
发包脚本也会跑:
|
||||
|
||||
```bash
|
||||
npm test -- --test-name-pattern='publish|space|billing'
|
||||
npm run verify:mindspace-publish-guards
|
||||
npm run verify:mindspace-publish-guards:full
|
||||
```
|
||||
|
||||
## 发版路径
|
||||
|
||||
本次没有从原始脏工作区发包,而是使用干净 worktree:
|
||||
|
||||
```bash
|
||||
git worktree add -b codex/rich-page-router /Users/john/Project/Memind-rich-page-router main
|
||||
git worktree add -b release/rich-page-router-0707 /private/tmp/memind-release-rich-page main
|
||||
```
|
||||
|
||||
发布前闸门:
|
||||
|
||||
```bash
|
||||
bash scripts/check-release-ready.sh --skip-fetch
|
||||
```
|
||||
|
||||
正式发布:
|
||||
|
||||
```bash
|
||||
bash scripts/release-portal-runtime-prod.sh --yes
|
||||
```
|
||||
|
||||
发布完成后推送 `main`,保证 manifest commit 可追溯:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## 关键踩坑:goosed bind mount 旧 inode
|
||||
|
||||
现象:
|
||||
|
||||
- goosed 原始会话显示 `sandbox-fs__write_file public/today-news-20260707.html` 成功。
|
||||
- 容器内能看到文件。
|
||||
- host 当前 `/Users/john/Project/Memind/MindSpace/.../public` 看不到文件。
|
||||
- 公网 URL 404 或被 `MEMIND_H5_HTML_FINISH_GUARD` 拦截。
|
||||
|
||||
原因:
|
||||
|
||||
Portal runtime 发布会移动旧 live 目录到 archive,再展开新 runtime live 目录。已经运行的 `goosed-prod-*` 容器仍握着旧目录 inode 的 bind mount。
|
||||
|
||||
确认命令:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
/opt/homebrew/bin/docker exec goosed-prod-1 sh -lc "ls -l /Users/john/Project/Memind/MindSpace/<user-id>/public/<file>.html"
|
||||
ls -l /Users/john/Project/Memind/MindSpace/<user-id>/public/<file>.html
|
||||
'
|
||||
```
|
||||
|
||||
修复:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
cd /Users/john/Project/goosed-prod
|
||||
/opt/homebrew/bin/docker compose -f docker-compose.prod.yml up -d --force-recreate
|
||||
'
|
||||
```
|
||||
|
||||
健康检查:
|
||||
|
||||
```bash
|
||||
ssh john@58.38.22.103 '
|
||||
/opt/homebrew/bin/docker ps --format "{{.Names}} {{.Status}}" | grep goosed-prod | sort -V
|
||||
for p in $(seq 18006 18014); do curl -kfsS "https://127.0.0.1:$p/status" >/dev/null || exit 1; done
|
||||
curl -fsS http://127.0.0.1:8081/api/status
|
||||
'
|
||||
```
|
||||
|
||||
经验:Portal runtime 发布后,如果本次涉及 MindSpace 页面写入,应该把 goosed remount 检查纳入发布后验收;必要时重建 goosed 容器。
|
||||
|
||||
## 下次 Checklist
|
||||
|
||||
1. 确认目标:是本地、103、还是 105 入口问题。
|
||||
2. 先只读确认生产 manifest、flags、health。
|
||||
3. 用真实生产账号做短消息 smoke。
|
||||
4. 用明确页面生成请求做 `write_file + URL 200` smoke。
|
||||
5. 用用户原始表达做路由 smoke,检查 `intent_routed`。
|
||||
6. 若 `direct_chat`:修 router 规则或 prompt,不要先开通用 code/tool gateway。
|
||||
7. 若有 `write_file` 但 URL 404:检查 host/container 同路径可见性,重点怀疑 goosed bind mount 旧 inode。
|
||||
8. 只从干净 release worktree 发包。
|
||||
9. 发包后验证 manifest、外部入口、真实业务页面链接。
|
||||
10. 推送 `main`,确保生产 manifest commit 可追溯。
|
||||
|
||||
## 不建议的做法
|
||||
|
||||
- 不要因为页面没生成就直接打开 `MEMIND_AGENT_CODE_RUNS_ENABLED` 或 `MEMIND_TOOL_GATEWAY_ENABLED`。
|
||||
- 页面发布主要应走 goosed + `sandbox-fs`,不是通用 code executor。
|
||||
- 不要只看 run `succeeded`,必须看是否有 `write_file` 和公网 URL 200。
|
||||
- 不要把 guard 拦截当作失败本身;guard 是保护用户不收到假链接,真正问题在路由或落盘。
|
||||
Reference in New Issue
Block a user