> **隔离说明:这份文档只讲生产 / 测试 / 预览边界和事故恢复,不是本地联调手册。要做本机开发,请看 [docs/local-dev.md](./local-dev.md)。** # 生产 / 测试 / 预览隔离规程 这份文档的目标只有一个:开发预览不能再影响生产。 ## 端口边界 | 环境 | 目录 | 用途 | 端口 | |------|------|------|------| | 生产 | `/Users/john/Project/Memind` | `g2.tkmind.cn` 当前在线服务(阿里云解析 → 105 → 本机 Mac) | `8081` | | 生产 MindSpace Service | `/Users/john/MindSpace` | MindSpace 独立 RPC / agent / asset 服务,供 Portal remote adapter 调用 | `8082` | | 生产 Plaza | `/Users/john/Project/Memind` + Plaza | `plaza.tkmind.cn` 当前在线服务 | `3001` | | 生产入口 | `/Users/john/Project/Memind/scripts/g2-lb.Caddyfile` | 105 转发入口 / 反代配置 | `8090` | | 测试 Portal | `/Users/john/Project/Memind` | 开发预览 API / Portal | `18081` | | 测试 Vite | `/Users/john/Project/Memind` | 开发预览前端 | `15173` | | 测试 Admin | `/Users/john/Project/Memind` | 开发预览后台 | `18082` | | 测试 Plaza | `/Users/john/Project/Memind` | 开发预览 Plaza | `13001` | | 测试 Ops | `/Users/john/Project/Memind` | 开发预览 Ops | `13002` | 硬规则: - 不在开发预览中使用 `8081`、`3001`、`8090`。 - 不在开发预览中使用生产 MindSpace service 的 `8082`。 - **禁止 SSH 到 `105` 直接改业务源码**;105 是入口/代理层,变更须本地 commit 后发布。见 [105 服务器变更规范](./105-server-operations.md)。 - 不在生产目录里跑会清理端口的开发脚本。 - 103 当前生产拓扑以 [103-runtime-topology.md](./103-runtime-topology.md) 为准;不要用旧文档里的 `/Users/john/Project/Memind/MindSpace` 推断当前 MindSpace Service 根目录。 - 不执行 `scripts/install-prod-services.sh` 来做开发预览;它会释放生产端口。 - 不手动 kill `8081` 上的进程,除非目标就是恢复/重启生产,并且已经确认影响窗口。 ## Harness 角色边界 Harness 是开发工具层,不是生产服务层。 正确关系: ```text Codex / Cursor / Goose 开发过程 -> harness 记忆、recall、context pack、审计 -> Memind / tkmind_go 开发 -> 测试通过后发布到生产 ``` 禁止关系: ```text g2.tkmind.cn 生产请求 -> 必须依赖 harness 才能运行 ``` 本机已有全局 harness: ```text /Users/john/Project/harness ``` 它应该嵌入 Codex、Cursor 这类开发工具,让项目开发有记忆: ```bash /Users/john/Project/harness/bin/codex-harness /Users/john/Project/harness/bin/install_cursor_hooks.sh /Users/john/Project/harness/bin/start_dashboard.sh ``` 开发时按项目目录区分记忆上下文: | 项目目录 | 记忆含义 | |----------|----------| | `/Users/john/Project/Memind` | 生产项目的开发记忆 | | `/Users/john/Project/Memind` | 测试项目的开发记忆 | | 测试 Goose 项目目录(按本机实际路径填写) | 测试 Goose 的开发记忆 | 硬规则: - 生产启动脚本不能依赖 harness 才能启动。 - 生产请求链路不能调用 harness 才能响应。 - 测试开发可以使用 harness,但不能写入或覆盖生产运行数据。 - 如果要做测试专用长期记忆,优先放在 `/Users/john/Project/test/harness` 或明确的 test 项目命名空间。 ## 当前生产如何确认 只读检查: ```bash cd /Users/john/Project/Memind lsof -nP -iTCP:8081 -sTCP:LISTEN curl -s http://127.0.0.1:8081/api/status cat .h5.pid ``` 期望: - `8081` 有 `node server.mjs` 监听。 - `/api/status` 返回 `ok`。 - `.h5.pid` 指向当前生产进程。 不要用下面这些命令做普通预览: ```bash pnpm dev pnpm dev:server pnpm start pnpm start:plaza node scripts/dev.mjs node server.mjs scripts/install-prod-services.sh ``` 这些命令如果没有端口隔离,可能占用或释放生产端口。 ## 下次开发要预览,怎么做 优先在测试目录进行: ```bash cd /Users/john/Project/Memind ``` 启动前先确认生产还在: ```bash curl -s http://127.0.0.1:8081/api/status lsof -nP -iTCP:8081 -sTCP:LISTEN ``` 再用测试端口启动预览: ```bash H5_PORT=18081 \ VITE_PORT=15173 \ ADMIN_PORT=18082 \ PLAZA_PORT=13001 \ OPS_PORT=13002 \ H5_PUBLIC_BASE_URL=http://127.0.0.1:15173 \ VITE_MINDSPACE_BASE=http://127.0.0.1:15173 \ pnpm dev ``` 访问地址: | 页面 | 地址 | |------|------| | H5 预览 | `http://127.0.0.1:15173/?preview=mindspace` | | 测试 Portal | `http://127.0.0.1:18081/api/status` | | 测试 Admin | `http://127.0.0.1:18082/healthz` | | 测试 Plaza | `http://127.0.0.1:13001/plaza` | | 测试 Ops | `http://127.0.0.1:13002/ops/` | 如果只改前端样式,优先只启动 Vite: ```bash cd /Users/john/Project/Memind VITE_PORT=15173 \ H5_PUBLIC_BASE_URL=http://127.0.0.1:15173 \ VITE_MINDSPACE_BASE=http://127.0.0.1:15173 \ pnpm dev:vite -- --host 127.0.0.1 --port 15173 ``` 这种方式不会碰 `8081`,适合做 UI 预览。 ## Goose 测试服务 生产 Goose 端口当前不要复用。测试 Goose 放在: ```text /Users/john/Project/test/test_tkmind_go ``` 测试 Goose 端口建议固定为: | 用途 | 端口 | |------|------| | 测试 goosed A | `18106` | | 测试 goosed B | `18107` | 测试 H5 如果要连测试 Goose,必须在测试目录 `.env` 中显式设置对应地址,不能指向生产 Goose。 ## goosed extension 堆积与文件句柄告警 如果 H5 / MindSpace 侧出现下面这类错误: ```text 会话策略同步失败:{"message":"Failed to add extension: IO error: Too many open files (os error 24)"} ``` 先不要直接重启生产服务。这个报错在本机更常见的含义是: - `goosed agent` 下面堆积了过多旧的 extension 子进程。 - macOS `launchctl limit maxfiles` 软上限偏低时,`/agent/add_extension` 会先撞到文件句柄上限。 - 如果恢复会话时无条件重启 agent,会把旧子进程堆得更快。 先做只读确认: ```bash launchctl limit maxfiles lsof -nP -iTCP -sTCP:LISTEN | rg '18006|18007|8081|3001|8090' for pid in 51613 51657; do echo "PID $pid $(ps -p $pid -o command=)" lsof -n -p $pid 2>/dev/null | wc -l done ps -axo pid,ppid,etime,command | rg 'mindspace-sandbox-mcp|mcp-server-fetch|goosed agent' ``` 判断口径: - `8081`、`3001`、`8090` 正常监听,说明主站未必有故障。 - 如果某个 `goosed agent` 的 FD 数已经逼近或超过 `256`,优先怀疑 extension 子进程堆积。 - 如果同一个 `MindSpace/` 在同一个 `goosed` 父进程下出现多个 `mindspace-sandbox-mcp.mjs`,通常只应保留最新一个。 安全处理顺序: 1. 先确认主站端口 `8081`、`3001`、`8090` 正常,不要把 route 问题误判成全站宕机。 2. 不先杀 `goosed agent` 主进程,也不要碰 `8081` 上的 `server.mjs`。 3. 优先只清理同父进程、同 `MindSpace/` 下重复堆积的旧 `mindspace-sandbox-mcp.mjs` 子进程,以及重复的旧 `mcp-server-fetch` 子进程。 4. 清理后立刻复查 `18006` / `18007` 的 `/status` 和 `goosed` FD 数,确认 agent 仍存活。 这次线上排查的经验值: - 两个 `goosed agent` 的 FD 数曾达到 `263` / `209`,而 `launchctl limit maxfiles` 软上限是 `256`。 - 仅清理重复 extension 子进程后,FD 数降到 `96` / `77`,`18006` 和 `18007` 的 `/status` 仍为 `ok`。 - 代码层已改为仅在会话策略真正变化时才触发 agent 重启,减少恢复会话时继续堆积 extension 子进程的概率。 自动巡检: - `scripts/monitor-goosed-fds.mjs` 每次检查 `goosed agent` FD 数和 `mindspace-sandbox-mcp.mjs` 子进程数。 - `scripts/install-goosed-monitor.sh` 会安装 `~/Library/LaunchAgents/cn.tkmind.goosed-monitor.plist`,默认每 60 秒运行一次。 - 默认策略只清理: - orphan 的 sandbox MCP 子进程; - 同一父进程、同一 `MindSpace/` 下超过 2 个的重复 sandbox MCP; - 总 sandbox MCP 数超过 80 时最旧的一批。 - 默认不按存活时间清理单个老进程,避免误伤长会话。 - goosed FD 超过 `GOOSED_FD_WARN=180` 只记录 warning;超过 `GOOSED_FD_RESTART=3200` 才滚动重启对应 goosed。 硬规则: - 不要因为 `Too many open files` 先去重启 `8081` 生产 Portal。 - 不要在未确认影响窗口前直接重启 `goosed agent` 主进程。 - 优先清理“明显重复”的 extension 子进程,而不是清空所有 agent 子进程。 ## 数据库口径 短期如果沿用同一个 RDS 库,只允许做 UI 和非破坏性流程预览。 不能在共用库时做这些事: - 改表结构。 - 跑迁移脚本。 - 批量清理数据。 - 测试支付回调、发布审核、权限变更等会污染真实用户状态的流程。 涉及数据结构或真实业务状态的开发,先建独立测试库,再预览。 ## 发布前流程 完整步骤、分步发布、105 同步与回滚见 **[生产更新发布指南](./release-deploy.md)**。 最小检查: 1. 在测试端口(如 `18081`)完成开发与预览;本机联调目录请看 [docs/local-dev.md](./local-dev.md),不要把这份规程当成开发启动手册。 2. `pnpm run build` + `pnpm test`。 3. `curl -s http://127.0.0.1:8081/api/status` 确认生产仍在线。 4. `./rsync_to_server.sh --dry-run` 预览 diff,确认后再执行正式发布。 ## 事故恢复最小步骤 如果发现 `g2.tkmind.cn` 异常,先检查本地主机生产: ```bash cd /Users/john/Project/Memind lsof -nP -iTCP:8081 -sTCP:LISTEN curl -s http://127.0.0.1:8081/api/status ``` 如果 `8081` 没有监听,再恢复本地主机 Portal: ```bash cd /Users/john/Project/Memind nohup /opt/homebrew/opt/node@24/bin/node server.mjs >> h5.log 2>&1 & echo $! > .h5.pid sleep 2 curl -s http://127.0.0.1:8081/api/status ``` 恢复后再检查入口反代: ```bash curl -s http://127.0.0.1:8090/api/status ```