Co-authored-by: Cursor <cursoragent@cursor.com>
17 KiB
TKMind Goose v1.49 迁移决策表
目标与边界
本次升级的目标是“升级运行时基线 + 选择性吸收 upstream”,不是把 TKMind 变成 vanilla Goose。
- Layer 0:PG Session、多实例 affinity、9 实例拓扑——保留。
- Layer 1:goosed 运行时、安全修复、agent loop——跟随 upstream v1.49。
- Layer 2:通用能力的 platform extensions——优先改为薄封装,逐项退役重复实现。
- Layer 3:
tkmind_compat、harness、sandbox-fs MCP——保留,确保 Portal/H5 契约与业务语义不变。 - Layer 4:Portal、微信、计费、MindSpace、Page Data——保留,属于产品层。
迁移完成的判定不是“upstream 代码已合入”,而是:新旧运行时在受保护业务路径上行为等价,且每个可退役定制都有 canary 证据与可执行回滚。
Memory 无缝迁移硬闸门
Memory 不能作为普通 extension 随运行时升级一起替换。这里必须分别保护四类状态,不能只比较“能否召回”这一项:
| 状态层 | 当前事实/责任 | v1.49 原则 |
|---|---|---|
| Goose Session | PG 共享 session、agent_session_id、SSE/Finish 状态 |
只适配 runtime API;不得让 upstream 本地 SQLite 成为隐式第二份真相。 |
| 业务记忆 | MySQL h5_conversation_messages、h5_user_memory_items、h5_episodic_memory_items |
保持原表、原 ID、原 user/session/evidence 关联与原写入语义;upstream 不得接管 owner。 |
| V2 候选/生命周期 | h5_memory_v2_candidates 及 promotion/expire/forget |
继续由 Portal policy 控制;off 必须零写入,所有操作按 user scope 严格隔离。 |
| 语义索引/项目记忆 | memory_embeddings、可选向量后端、harness bootstrap/remember |
只能是可重建派生数据或受控项目记忆;必须保留来源 ID、embedding 版本和回填水位,不能反向覆盖业务记忆。 |
以下条件未全部满足时,Memory owner 必须保持现有 conversation-memory / Portal 链路,v1.49 只能旁路 shadow 观察:
- 同一
userId能在旧运行时、新运行时、Portal 和所有 Memory adapter 中稳定映射;不能用 display name、邮箱或临时 session ID 代替。 agent_session_id、source_session_id、evidence_message_id、memoryid、memory_hash的映射已导出并逐项对账;不能因 session ID 重建造成重复记忆或丢 evidence。- 迁移期间只有一个业务记忆写入 owner。禁止旧链路和 upstream 同时写入同一事实,避免重复提取、hash 冲突和不可逆覆盖。
- 新 runtime 先双读/对照旧结果,再考虑切换 resolve;
write、compact、promotion、expire、forget 仍走旧 owner,直到对账稳定。 - 已建立按用户的 count、ID 集合、hash、状态、更新时间、来源 session 和 evidence 外键对账;总数相同不能替代逐用户逐 ID 对账。
- 已覆盖增量追平:固定 cutover watermark,回填期间捕获 watermark 之后的新写入;不能依赖从
updated_at=0开始的有限批次。 - 向量索引记录 embedding provider/model/version/dimensions、文本规范化版本、来源 memory ID 和写入时间;维度或模型变化必须新建 namespace/table 或完整重建,禁止混索引。
- 向量不可用、索引落后、embedding 失败、upstream 超时或结果不一致时,必须 fail-open 回到 legacy exact/recent recall;不得阻塞聊天或删除原记忆。
remember-recent使用原始用户消息(优先h5_agent_runs.user_message_json),不得把注入的[Memory Context]再提取成新记忆;空数组必须表示“无需保存”。- 回滚只切回 resolve/write/compact owner 与 feature flag,不删除候选表、向量表或原始记忆;回滚后仍能从原始表重建派生索引。
Memory 数据不对接上的常见盲点
- 只迁 goosed session,不迁业务记忆关联:session 恢复成功不代表
userId → source_session_id → evidence_message_id仍然成立。 - 把向量库当主库:向量结果是派生索引,不能替代 MySQL 事实、状态和 forget/expire 语义。
- 双写但没有幂等键:必须以稳定的业务 memory ID/hash + user namespace 幂等 upsert;不能用每次 embedding 的随机 ID。
- 只比总数不比内容:同数但 user 串线、status 丢失、evidence 外键断裂或更新时间截断,都会表现为“数据在但接不上”。
- 忽略提取输入污染:新 runtime 注入的 Memory Context 可能被再次保存,造成旧记忆复制和权重膨胀。
- 把 schema/DDL 当启动副作用:本地可以显式建空表并验证;生产 schema、备份、回填和 owner 切换必须是独立审批步骤,本次升级禁止触碰 103。
本地优先与生产隔离规则
本次迁移只能在本地 loopback、隔离数据库和临时 workspace 开始。未获得新的明确授权前,禁止:
- 连接 103、105 或任何生产数据库、生产 goosed、生产 MindSpace 服务;
- 执行生产发布、runtime/artifact 生成、远端迁移、远端 backfill、远端 schema DDL 或生产重启;
- 使用生产
DATABASE_URL、GOOSE_SESSION_DB_URL、Memory provider key 或生产数据做测试; - 将 upstream Memory backend 接入 Portal 主路径,或修改 production profile 的 Memory 开关。
本地测试必须使用显式 loopback 地址和隔离数据库名,例如 127.0.0.1 的 MySQL/PG;测试启动前应打印并检查目标 host、database、runtime profile,命中 103、105、公网数据库或 production profile 时直接失败。优先使用脱敏 fixture 或本地数据副本,测试结束删除临时用户/会话/索引,但保留可复核的对账报告。
决策规则
| 判断问题 | Replace:upstream 替代 | Keep:保留定制 | Thin wrapper:薄封装 upstream |
|---|---|---|---|
| 是否 TKMind 独有? | 否 | 是 | 部分独有 |
| upstream 是否明显更成熟? | 是 | 否或不对等 | 是,但缺 TKMind 钩子 |
| 替换后 H5/微信契约是否不变? | 是 | 不适用 | 需要 adapter 层 |
| 是否绑定 MindSpace、计费或权限? | 否 | 是 | 部分绑定 |
只有“非独有 + upstream 更强 + 契约可不变”同时成立时,才直接 Replace。任何涉及用户空间、发布、计费、权限、微信 ACK 或 PG Session 的能力,默认先 Keep;若能复用 upstream 内核,采用 Thin wrapper。
定制项决策表
状态含义:Candidate 表示建议方向,尚未凭 canary 取得退役资格;Keep 表示本次不以 upstream 替代;Wrapper 表示保留入口与契约,内部逐步换内核。
| 定制项 | 决策 | v1.49 迁移动作 | 退役/切换门槛 | 回滚点 |
|---|---|---|---|---|
platform_extensions/web.rs(DuckDuckGo + fetch) |
Wrapper → Replace | 保留现有 extension 名与 H5 调用入口;内部优先转发 upstream web-search / browser-use skill。 |
搜索、抓取、超时、fallback、权限与结果格式回归通过;无 TKMind 专属域规则回归。 | 切回旧 extension 实现。 |
session-reply-wait 与 Portal 侧 DeepSeek workaround |
Wrapper / Keep(分阶段) | 先在 v1.49 保留等待、Finish、重试与游标适配;对 upstream reasoning 修复做独立 canary。 | 工具调用后的 reasoning_content、SSE Finish、断线续播、重复消息与超时回归通过。 |
保留 session-reply-wait 与旧兼容链路。 |
DeepSeek no-think 代理(:18036) |
Wrapper → Pass-through | 先保留独立代理与端口;验证 v1.49 原生兼容后,再将代理改为 pass-through,最后才评估停用。 | 生产同构工具轮次通过,候选失效时可自动回落稳定版本;不得仅凭版本号拆除。 | 恢复旧 :18036 no-think 处理。 |
billing-token-state 粗估价 |
Candidate Replace / Simplify | 优先读取单条消息 usage/cost 与 cache token;accumulated_cost 稳定时,Portal 仅保留缺失字段时的保守估算。 |
Finish 稳定带完整累计成本;账单、margin、补偿与幂等回归通过;不能出现超扣。 | 恢复 Portal 估算逻辑。 |
| 部分 message sanitize 补丁 | Candidate Replace | 对比 v1.49 provider message 处理与本地补丁 diff;只删除已被 upstream 覆盖且契约等价的分支。 | DeepSeek、多模态隔离、工具轮次、历史消息显示回归通过。 | 恢复对应 sanitize 分支。 |
| PG Session 多实例共享 | Keep | 移植到 v1.49 session/runtime 接口;保持 PG、9 实例 affinity 与共享 session 语义。 | 多实例并发、重启恢复、session 一致性与故障切换通过。 | 保留旧 PG adapter / 配置。 |
tkmind_compat(/sessions 等 REST adapter) |
Keep | 仅适配 upstream v1.49 内部 API;对外 REST/H5 契约不变。 | Portal、H5、微信客户端不需要改请求格式;旧客户端回归通过。 | 切回旧 goosed adapter。 |
tkmind_memory(harness bootstrap/remember) |
Keep / Wrapper | 保留项目记忆与 capability 门控;若 upstream 有 projects/memory 原语,只在内部复用,不改变 TKMind 入口。 | bootstrap、remember、权限边界与项目级记忆回归通过。 | 使用现有 harness 实现。 |
sandbox-fs MCP(MindSpace、Page Data、generate_image) |
Keep | 按 v1.49 extension/MCP 生命周期接口移植;保持路径隔离、页面落盘、缩略图 fallback 与图片策略。 | 发布/改页、Page Data、图片生成、私有路径隔离、Transport closed 重试回归通过。 | 保留现有 sandbox-fs 子进程与 Portal fallback。 |
| aider / openhands platform extensions | Keep | 保持 coding_router、微信与 agent-run 策略;只适配运行时加载接口。 |
coding 路由、模型名映射、权限与失败回退不变。 | 保留旧 extension。 |
custom_tkmind_relay_deepseek |
Keep | 保留计费、margin、Admin 模型中心绑定;若 upstream provider 足够,只替换内部传输实现。 | provider/key/model 目录、计费归属、margin 与 canary 策略回归通过。 | 切回 custom relay。 |
capabilities.mjs + extension policy |
Keep / Wrapper | 保留用户权限、canary、微信策略;可将通用 extension 解析委托给 upstream。 | capability 不越权,sandbox-fs 与 read_image 等关键策略不回归。 |
使用现有 policy builder。 |
明确不替换的产品契约
以下能力不能因为 upstream 1.49 提供了相似原语就直接删除:
- MindSpace 页面发布、
edit_file落盘、Finish 合并与 Page Data 交付。 sandbox-fs的 workspace 路径隔离、图片生成、缩略图 fallback 与 MCP 失败兜底。- 微信 5 秒 ACK、消息路由、页面链接验证与定时任务投递。
- 计费、usage/cost、margin、provider/model 权限与 canary 策略。
- PG Session 共享、9 实例 affinity、Portal/H5
/sessions等兼容 REST。 - SSE 断线续播、Portal replay ID 与 Goose
Last-Event-ID的映射。 - 私有 MindSpace 页 noindex、confirmed public 才可收录的 SEO/GEO 策略。
分阶段迁移清单
Phase 0:冻结基线
- 记录当前 1.41/现生产候选的 commit、runtime artifact、配置与端口。(见 docs/goose-v149-local-phase0-manifest.md)
- 拉取 upstream
v1.49.0;创建upgrade/v1.49-tkmindworktree(/Users/john/Project/tkmind_go-v149)。 - 本地隔离 PG 库
goose_sessions_v149_dev;启动脚本scripts/run-goosed-v149-local.sh(端口18049)。 - 建立旧版与 v1.49 的 canary 开关,以及一键回落路径(
scripts/switch-goose-v149-canary.sh+ docs/goose-v149-canary-runbook.md)。 - 保存关键业务场景的输入、SSE 事件、Finish 状态、usage/cost 与产物校验结果(
node scripts/capture-goose-v149-baseline.mjs+node scripts/check-goosed-v149-all.mjs)。 - 导出本地基线 Memory manifest:每用户 memory ID/hash/status/source session/evidence、候选状态、向量 namespace/version(
node scripts/export-goose-v149-memory-manifest.mjs)。 - 记录 schema 版本、索引版本、回填 watermark、时区和字符规范化规则(见 manifest
schemaVersion/cutoverWatermarkMs/textNormalization字段)。
Phase 1:运行时升级
- 将 upstream v1.49 安全修复、agent loop 与 provider 变更移植到 Layer 1(基线已在 v1.49.0 tag)。
- 适配
tkmind_compat、PG Session、harness 的加载/生命周期接口(P0 路由 + PG 双后端)。 - 适配 sandbox-fs 的加载/生命周期接口(
extension_overrides+/agent/tools验证)。 - 自定义 provider 路由(
/config/custom-providers、/config/upsert)+custom_tkmind_relay_deepseek本地 reply Finish smoke。 - aider / openhands platform extensions +
deploy/coding_router.sh(check-goosed-v149-executors.mjs)。 - v1.49 初始配置强制
MEMORY_BACKEND=legacy、MEMORY_VECTOR_ENABLED=0、lifecycle/promotion/injection 为 off/shadow(run-goosed-v149-local.sh+check-goosed-v149-memory-policy.mjs)。 - 先验证旧 owner 在 v1.49 上完成“写入 → compact → 新 session resolve”的本地闭环(
check-goosed-v149-memory-loop.mjs)。 - 保持 Layer 3–4 对外接口不变(Portal canary 联调 ✅;
POST /agent/resume、GET /sessions/{id}带 conversation、restart 返回extension_results已补齐 smoke)。
Phase 2:逐项 canary
- web/search:TKMind
platform/web已移植至 v1.49(check-goosed-v149-web-smoke.mjs);upstreamweb-searchskill 对照仍保留。 - DeepSeek:工具调用 Finish(
check-goosed-v149-deepseek-tools.mjs);reasoning 保留单测(check-goosed-v149-thinking-preservation.mjs✅);:18036no-think 代理与 Portal replay 仍保留。 - upstream web-search:
load_skill路径 smoke(check-goosed-v149-skills-smoke.mjs);TKMindplatform/web仍为主 H5 契约。 - cost:
usage、cache token、accumulated_cost、估算回退与账单金额对照(check-goosed-v149-cost-smoke.mjs→docs/baselines/goose-v149-cost-evidence-*.json)。 - message sanitize:provider 层单测 v1.41≈v1.49(
compare-goose-v149-message-sanitize.mjs✅);Portal 多模态/历史展示路径仍保留对照。 - Memory 对账:逐用户逐 ID/hash/status/source/evidence 对照(
compare-goose-v149-memory-manifest.mjs✅)。 - Memory 故障演练:legacy owner 超时 fail-open 不中断(
check-goosed-v149-memory-failopen.mjs);向量→legacy 自动回退待 pgvector canary 时再验。 - 每项记录“通过 / 保留 / 回滚”,不得一次性批量删除(台账:docs/goose-v149-phase2-evidence.md)。
Phase 2 聚合入口: node scripts/run-goosed-v149-phase2.mjs
Phase 3 收口入口: node scripts/run-goosed-v149-phase3.mjs · 清单:docs/goose-v149-phase3-closeout.md
Phase 3:退役与收口
- 只删除已取得 canary 证据的重复逻辑(当前:无删除项;见 Phase 3 清单)。
- 保留薄封装入口、feature flag 与回滚说明(canary runbook +
switch-goose-v149-canary.sh)。 - 更新 runtime 文档、证据链接与 Phase 3 合并闸门(
docs/goose-v149-phase3-closeout.md、docs/baselines/README.md)。 - 本地连续验证 + Memory manifest 无 drift(
run-goosed-v149-phase3.mjs);不授权 103 操作。 - 合并前运行影响域 verify;完成测试后再讨论 commit、合并或发布(须用户明确批准)。
必须通过的回归守卫
涉及受保护路径时,至少执行仓库约定的:
npm run verify:mindspace-publish-guards
npm run verify:mindspace-publish-guards:full
npm run verify:mindspace-page-sync-guards
npm run verify:h5-session-patches
npm run verify:seo-geo
npm run verify:seo-discovery
npm run verify:page-data
DeepSeek 兼容链路还必须覆盖 release-gate/deepseek-production-parity.test.mjs 及对应生产影响域场景。任何守卫失败时,结论只能是“保留定制并修复适配”,不能以“upstream 已支持”为理由继续退役。
Memory 本地最低验证集:
npm run check:memory-v2-contracts
npm run check:memory-v2-config
npm run check:memory-v2-session
npm run check:memory-v2-stack
node --test conversation-memory.test.mjs conversation-repair.test.mjs \
memory-v2-personal-store.test.mjs memory-v2-lifecycle.test.mjs \
memory-v2-pgvector-backfill.test.mjs memory-v2-runtime.test.mjs
以上命令只验证本地代码/fixture;若要使用真实本地 MySQL/PG,必须先确认连接字符串是 loopback 隔离库,并把 count、ID/hash、关联和 watermark 对账结果作为附件保存。任何测试失败、数据 drift、owner 不明确或无法证明回滚时,Memory 迁移保持 blocked,不得进入生产。
一句话结论
跟随升级,但不做 upstream 全量替换:通用能力(搜索、浏览器、hooks、cost 追踪、DeepSeek 兼容)按证据逐步 Replace 或 Wrapper;MindSpace、Page Data、微信、计费、权限、PG 多实例与 H5/Portal 契约必须 Keep。