# Data Repository Persistence 追踪

## 2026-04-19

### 阶段：legacy local-plan 迁移建档（RP-001）

- 复核当前工作树后确认：根目录 `local-plan/` 只剩一份
  `settings-persistence-serialization-tracking.md`，它同时承担 todo 与 trace 角色
- 按 `ai-plan` 治理规则建立 `ai-plan/public/data-repository-persistence/` 主题目录，并补齐：
  - `todos/`
  - `traces/`
  - `archive/todos/`
  - `archive/traces/`
- 将旧混合文件拆分为主题内历史跟踪归档与基于同一材料整理的历史 trace，active 入口只保留当前恢复点、
  活跃事实、风险与下一步
- 在 `ai-plan/public/README.md` 中建立
  `feat/data-repository-persistence` -> `data-repository-persistence` 的 worktree 映射
- 同步更新 `ai-plan-governance` 的 tracking / trace，记录 legacy 单文件计划也已按新目录语义收口

### Archive Context

- 历史跟踪归档：
  - `ai-plan/public/data-repository-persistence/archive/todos/data-repository-persistence-history-pre-rp001.md`
- 历史 trace 归档：
  - `ai-plan/public/data-repository-persistence/archive/traces/data-repository-persistence-history-pre-rp001.md`

### 下一步：JsonSerializer 配置契约补充

1. 后续继续该主题时，只从 `ai-plan/public/data-repository-persistence/` 进入，不再恢复 `local-plan/`
2. 若 active 入口再次积累多轮已完成且已验证阶段，继续按同一模式迁入该主题自己的 `archive/`

## 2026-04-20

### 阶段：JsonSerializer 配置契约补充（RP-001）

- 复核 `GFramework.Game/Serializer/JsonSerializer.cs` 后确认：当前实现直接复用传入的 `JsonSerializerSettings`，并通过 `Settings` / `Converters` 暴露活动配置对象
- 复核 `docs/zh-CN/game/serialization.md` 后确认：现有 FAQ 把 `JsonSerializer` 写成“本身线程安全”，与当前可变配置契约不一致
- 决定本轮只补齐契约说明而不改变运行时行为：
  - 在源码 XML docs 中说明 settings / converters 的生命周期与并发约束
  - 在定向单测中固定“序列化器暴露活动配置实例”的当前契约
  - 在 `docs/zh-CN/game/serialization.md`、`docs/zh-CN/game/index.md` 与 `GFramework.Game/README.md` 中同步修正接入建议

### 下一步：JsonSerializer 配置契约补充

1. `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~JsonSerializerTests"` 已通过（9/9）
2. 验证过程中出现的 analyzer warning 为仓库既有 warning，未在本轮扩大
3. 下一步回到 migration abstraction 与 codec / persistence pipeline 的后续评估

### 阶段：迁移执行器统一收敛（RP-001）

- 对 `SettingsModel`、`DataRepository`、`UnifiedSettingsDataRepository`、`SaveRepository<T>` 的实现进行并排核对后确认：
  - `DataRepository` 与 `UnifiedSettingsDataRepository` 不直接承担按版本号推进的迁移链
  - 实际重复点只在 `SettingsModel` 与 `SaveRepository<T>` 的“版本迁移链执行与校验”逻辑
- 决定不新增 public migration abstraction，而是抽出 internal `VersionedMigrationRunner`
  - 统一前向注册校验
  - 统一缺链失败
  - 统一声明目标版本与实际结果版本一致性校验
  - 统一非递增 / 超目标版本防护
- `SettingsModel` 本轮额外补强：
  - 拒绝同一设置类型同一 `FromVersion` 的重复注册
  - 以当前内存设置实例的 `Version` 作为目标运行时版本
  - 迁移失败时保持当前实例不被旧数据覆盖，并继续记录错误日志
- `SaveRepository<T>` 改为复用同一个 internal runner，但保留“加载成功后自动回写升级结果”的现有仓库语义
- 同步更新 `docs/zh-CN/game/setting.md` 与 `docs/zh-CN/game/data.md`，补迁移链约束说明
- 新增 / 更新测试：
  - `SettingsModelTests`：重复注册拒绝、不完整链路保持当前实例、缓存失效场景
  - `PersistenceTests`：迁移结果版本与声明版本不一致时显式失败

### 验证：迁移执行器统一收敛

1. `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~SettingsModelTests|FullyQualifiedName~PersistenceTests"` 已通过（20/20）
2. 过程中出现的 analyzer warning 来自仓库既有项，未在本轮扩大

### 下一步：迁移执行器统一收敛

1. 进入 codec / persistence pipeline 边界评估
2. 重点查看压缩、加密、元数据、备份是否仍然跨越 `Serializer` / `Storage` / `Repository` 多层分散

### 阶段：PR #260 review follow-up（RP-001）

- 复核当前 PR review 后确认两条未解决 inline 线程仍成立：
  - `SaveRepository<T>.MigrateIfNeededAsync` 在每一步迁移时都现查 `_migrations`，会让并发 `RegisterMigration`
    把同一次加载暴露给变化中的迁移链
  - `VersionedMigrationRunner.MigrateToTargetVersion` 的 XML docs 仍缺少参数校验异常契约
- 同步接受两条 outside-diff / nitpick 中仍然成立且低成本的 follow-up：
  - `SettingsModel.RegisterMigration` 与 `MigrateIfNeeded` 需要补齐 XML 文档，和当前迁移约束保持一致
  - `PersistenceTests` 需要锁定“迁移失败后不会污染已持久化存档”的行为
- 额外复核 `docs/zh-CN/game/index.md` 后确认：最低接入示例仍把 `TypeNameHandling.Auto` 用在用户可编辑的存档场景，
  这与当前仓库安全约束不一致，因此一并改为默认安全配置并补充白名单说明
- 本轮实现计划：
  - `SaveRepository<T>` 在加载前复制迁移表快照，再把 resolver 切换到快照读取
  - 新增并发回归测试，证明加载过程不会在迁移途中读到后续注册的链路
  - 补齐 `VersionedMigrationRunner` / `SettingsModel` XML docs
  - 更新 `docs/zh-CN/game/index.md` 示例与 active tracking
- 实际落地结果：
  - `SaveRepository<T>` 已切换为在加载前复制 `_migrations` 快照，并在同一次迁移链执行中只读取快照
  - `VersionedMigrationRunner`、`SettingsModel.RegisterMigration` 与 `SettingsModel.MigrateIfNeeded` 已补齐缺失 XML docs
  - `PersistenceTests` 已新增“迁移失败不污染持久化数据”断言，以及并发注册下固定迁移快照的回归测试
  - `docs/zh-CN/game/index.md` 的 `JsonSerializer` 接入示例已改为 `TypeNameHandling.None`，并补充白名单 binder 说明

### 验证：PR #260 review follow-up

1. `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~SettingsModelTests|FullyQualifiedName~PersistenceTests" -m:1 -nodeReuse:false` 已通过（21/21）
2. 本次验证未再出现本轮新增的 XML doc warning；输出中的 analyzer warning 仍为仓库既有项

### 下一步：PR #260 review follow-up

1. 回到 codec / persistence pipeline 边界评估
2. 继续判断压缩、加密、元数据与备份策略是否需要新的 dedicated pipeline abstraction

### 阶段：SettingsModel 迁移缓存并发修复（RP-001）

- 重新使用 `$gframework-pr-review` 复核 PR #260 后确认：此前遗漏了一条仍然 open 的 `SettingsModel.cs` major comment，
  问题点不是迁移执行本身，而是 `_migrations` 与 `_migrationCache` 在并发注册和 cache miss 重建交错时，可能把旧快照写回缓存
- 确认该 comment 来自 `2026-04-20T04:23:09Z` 的 CodeRabbit review run；当前修复策略采用同一把私有锁串行化：
  - `RegisterMigration` 中的 `_migrations.TryAdd(...)` 与 `_migrationCache.TryRemove(...)`
  - `MigrateIfNeeded` 在 cache miss 时按类型重建 `versionMap` 并写回 `_migrationCache`
- 同步补充源码注释与 XML remarks，明确运行时注册与缓存重建共享同一并发语义
- 计划新增 `SettingsModelTests` 回归测试，验证 cache rebuild 与运行时注册在同一把锁前排队后，后续初始化能看到新增迁移而不会留下 stale cache

### 验证：SettingsModel 迁移缓存并发修复

1. `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~SettingsModelTests" -m:1 -nodeReuse:false` 已通过（5/5）
2. 本轮验证中未再出现 `SettingsModel` 新增的 nullable warning；输出中的 analyzer warning 仍为仓库既有项加上新的 `MA0158`
   建议，后者来自本轮新增对象锁

### 下一步：SettingsModel 迁移缓存并发修复

1. 若继续收口 analyzer 反馈，可评估是否将 `_migrationMapLock` 升级为 `System.Threading.Lock`，同时保留可验证的并发回归测试策略
2. 否则恢复到 codec / persistence pipeline 边界评估
