package memory // migrate.go 实现 Memory frontmatter 格式迁移骨架. // // [模块定位] // 与 pkg/engine/migrate.go 对称--为记忆文件 frontmatter 格式的未来 // breaking change 提供规范的迁移路径. // // [当前状态] // frontmatterMigrations 为空.没有历史债,无需实际迁移函数. // 骨架的价值在于: // 1. 规范化:未来开发者知道在哪里加迁移函数 // 2. 调用点预置:ScanMemoryDir 已预留 migrateFrontmatter 调用 // 3. 强制幂等:所有迁移函数必须满足幂等约定(文档化而非运行时强制) // // [未来示例] // 假设 v2 新增 `conflict: bool` 字段,且我们决定将 "feedback" 类型 // 重命名为 "guidance"(breaking change): // // func init() { // frontmatterMigrations[1] = func(fm *Frontmatter) error { // // v1 → v2:feedback → guidance 类型重命名 // if fm.Type == "feedback" { // fm.Type = "guidance" // } // return nil // } // } // // 然后将 frontmatterCurrentVersion bump 为 2,Save() 写入的新文件自动变为 v2. // // 升华改进(ELEVATED): 早期实现 没有 memory frontmatter 的版本管理. // 插件 schema 的 V1/V2 是事后补救,需要 Union type 兼容两种格式, // 代码复杂度高.我们从一开始就加 version 字段,未来迁移只需注册函数, // 不需要改读取逻辑. // 替代方案:<发现破坏性变更时再加版本字段> - // 否决原因:发现时已有无版本的旧文件无数,反而更难处理(早期实现 的教训). import "fmt" // FrontmatterMigrateFunc 是一个 frontmatter 迁移函数的类型. // 输入是版本 N 的 Frontmatter,函数就地修改为版本 N+1 的格式. // // 约定: // - 幂等:重复调用不产生副作用 // - 无损:不丢失原有字段的语义信息 // - 不修改 Version 字段:migrateFrontmatter 统一递增 type FrontmatterMigrateFunc func(*Frontmatter) error // frontmatterMigrations 是 frontmatter 迁移函数注册表. // // key 是"从版本 N 迁移到版本 N+1"的 N. // 当前为空--没有历史债,无实际迁移需求. var frontmatterMigrations = map[int]FrontmatterMigrateFunc{} // migrateFrontmatter 将 Frontmatter 从当前版本迁移到 frontmatterCurrentVersion. // // 设计与 engine.migrateTranscript 完全对称: // - 按版本递增顺序执行注册函数 // - 缺少某版本的迁移函数 → 返回错误(而非跳过) // - 当前迁移表为空,此函数是 no-op // // 精妙之处(CLEVER): 当 fm.Version == frontmatterCurrentVersion 时, // for 循环条件直接不成立,函数立刻返回 nil--零开销空迁移. // 每条记忆读取时都会调用此函数,性能必须保证. // // 历史包袱(LEGACY): 同 engine/migrate.go 的相同问题--fn(fm) 就地修改 Frontmatter, // 中途失败无法自动回滚.当前无实际影响(没有注册的迁移函数,且失败时调用方不持久化). // 未来编写真实迁移函数时:先校验后修改,或在函数内部自行备份还原. // 改进条件:注册第一个真实迁移函数时. func migrateFrontmatter(fm *Frontmatter) error { for fm.Version < frontmatterCurrentVersion { fn, ok := frontmatterMigrations[fm.Version] if !ok { return fmt.Errorf( "migrate frontmatter: no migration registered for v%d → v%d "+ "(current engine supports up to v%d)", fm.Version, fm.Version+1, frontmatterCurrentVersion, ) } if err := fn(fm); err != nil { return fmt.Errorf("migrate frontmatter v%d: %w", fm.Version, err) } fm.Version++ } return nil }