name: cheat-migrate
description: 把老用户的 .cheat-state.json 升级到当前 schema_version。读 migrations/registry.md 算迁移链,按顺序应用每一步迁移文件。幂等:跑两次结果一样。失败停在中间版本不前进。触发词:"迁移"/"升级 state"/"migrate"/"我的 state 是老版本"/"schema 版本不对"。
argument-hint: [— from: ] [— to: ] [— dry-run]
allowed-tools: Bash(*), Read, Write, Edit, Skill
/cheat-migrate — Schema 版本迁移
把用户 .cheat-state.json 从旧 schema_version 升级到 cheat-on-content 当前期望的 LATEST_SCHEMA。
Overview
[用户:迁移 / 或 SessionStart 提示后用户跑]
↓
[Phase 0: 读 .cheat-state.json + migrations/registry.md → 确定迁移链]
↓
[Phase 1: dry-run(默认)展示迁移计划,等用户确认]
↓
[Phase 2: 备份 .cheat-state.json → .cheat-state.json.backup-<timestamp>]
↓
[Phase 3: 按顺序对每个 step 应用对应迁移文件的 HOW 段]
↓
[Phase 4: 验证升级后 state 文件能被解析 + schema_version 已更新]
↓
[Phase 5: 报告 + 提示如有备份需要清理]
Constants
- REGISTRY_PATH =
${SKILL_DIR}/../../migrations/registry.md— 版本链单一来源 - MIGRATIONS_DIR =
${SKILL_DIR}/../../migrations/— 迁移文件目录 - DRY_RUN_BY_DEFAULT = true — 首次跑展示计划,不直接改文件
- BACKUP_BEFORE_WRITE = true — 写之前必备份;备份文件保留至下次成功 init / 用户手动清理
- STOP_ON_STEP_FAILURE = true — 任何 step 失败 → 停在中间版本,不前进,不回滚
💡 调用时覆盖:
/cheat-migrate — dry-run: false直接执行 //cheat-migrate — to: 1.2仅升到指定版本
Inputs
| 必填 | 来源 |
|---|---|
.cheat-state.json |
用户项目根 |
migrations/registry.md |
LATEST_SCHEMA + 版本链表 |
migrations/<from>-to-<to>.md |
每步具体迁移指令 |
Workflow
Phase 0: 确定迁移链
- 读
.cheat-state.json→ 解析current_version = state.schema_version - 读
migrations/registry.md→ 解析LATEST_SCHEMA字段(行:LATEST_SCHEMA = "X.Y") - 解析
args.to覆盖(如有);否则 target = LATEST_SCHEMA - 解析
args.from覆盖(罕见场景:用户的 state 文件 schema 字段坏了,强制指定起点) - 状态判断:
current_version == target→ 输出"✅ state 已是 {target},无需迁移" → 退出current_version > target(比如用户跑了 dev 版又切回 release)→ 报错"无法降级,请手动调整或重新 init"current_version < target→ 继续,从注册表查出迁移链
- 从注册表"版本链"表算出
chain = [(from, to, file), ...],按顺序串起 current → target
如果某一步在注册表里缺失(比如 current_version 不在表里)→ 报错并展示"目前已知版本:[1.0, 1.1, ...]",让用户检查。
Phase 1: dry-run
输出迁移计划:
📋 迁移计划
当前版本: 1.0
目标版本: 1.2
将按顺序跑 2 步:
[1/2] 1.0 → 1.1(MINOR)
新增字段:typical_duration_seconds, target_publish_cadence_days, ...(共 12 字段)
删除字段:mode, prediction_complexity, bucket_scheme
详见: migrations/1.0-to-1.1.md
[2/2] 1.1 → 1.2(MINOR)
新增字段:[...]
详见: migrations/1.1-to-1.2.md
⚠️ 备份位置: .cheat-state.json.backup-<timestamp>
继续吗?回 yes 执行 / no 退出 / dry-run-detail 看每步具体改什么。
如 args["dry-run"] == false 或用户回 yes → 进 Phase 2。
Phase 2: 备份
cp .cheat-state.json .cheat-state.json.backup-$(date +%s)
输出:"📦 备份到 .cheat-state.json.backup-1714838400"
Phase 3: 按顺序应用每步
对 chain 里的每个 (from, to, file):
- 输出 "→ [N/M] 应用 {file}..."
- 读
migrations/<file>→ 找到## HOW (Claude steps for /cheat-migrate)段 - 按段内自然语言步骤逐项执行——这是关键:迁移是 Claude 读 markdown 跑的,不是 python 脚本
- 每步完成后:
- 更新内存里的 state.schema_version = to
- 原子写到磁盘(写 .tmp → rename)
- 如某步失败:
- 输出"❌ {file} 第 N 步失败:{error}"
- 不前进、不回滚(state 已停在前一步成功的中间版本)
- 提示:"已停在 schema_version: {last_success_version}。修复后重跑 /cheat-migrate 会从这里继续"
- 退出
Phase 4: 验证
升完后:
- 读
.cheat-state.json→ 解析 → 应能成功 - 检查
schema_version == target - 检查所有"必填字段"非缺失(参照 shared-references/state-management.md 完整 schema)
- 失败 → 报错"迁移完成但验证失败:{detail}。state 文件可能不一致——查看备份恢复"
Phase 5: 报告
✅ 迁移完成
从: 1.0
到: 1.2
应用步骤: 2
state 文件现在含 X 字段,全部通过验证。
📦 备份保留:.cheat-state.json.backup-1714838400
(确认一切正常后可手动 rm;下次成功 /cheat-init 也会清理过期备份)
下一步建议:
- 跑 /cheat-status 确认看板正常
- 如有 hooks 重装需求,跑 bash <skill_repo>/install.sh --reinstall-hooks
Key Rules
- 幂等:在已升过的 state 上重跑应该立刻退出"无需迁移",不重复应用步骤。靠对比
current_version == target实现 - 不跳版:1.0 → 1.3 必须按 1.0→1.1→1.2→1.3 顺序,每步独立可恢复。不允许"直接升 1.0 → 1.3 的合并 migration"
- 不静默兼容:state 文件 schema_version 不识别 → 明确报错"未知版本 X,最近已知版本 Y",不假装能继续
- 失败停在原地:第 N 步失败时 schema_version 停在 N-1 已成功的版本,不回滚到迁移前。重跑能从断点继续
- 备份是硬约束:写之前必有备份。即使用户跑
--dry-run: false,备份动作仍执行 - 不动 predictions / rubric / videos:只改
.cheat-state.json。其他用户数据由各自 skill 负责,迁移 skill 不碰 - MAJOR vs MINOR 透明:dry-run 输出必标 (MAJOR) / (MINOR)。MAJOR 时额外提示"老 skill 用旧字段读会出问题,迁移完不能回退到老 skill 版本"
Refusals
- 「跳过 dry-run,立刻覆盖我的 state」 → 允许(
--dry-run: false),但备份仍强制执行 - 「我的 state 损坏了 / schema_version 字段没了,能不能猜一个版本来跑」 → 允许指定
--from: 1.0,但要警告"基于猜测的迁移可能导致字段错位" - 「降级到旧版本(current > target)」 → 拒绝。schema 演进单向。要降级请手动 cp 历史 git 快照
- 「合并多步迁移成一个 atomic」 → 拒绝。每步独立可恢复是设计核心
- 「在跑 cheat-bump / cheat-predict 中途调 migrate」 → 拒绝。等其他 skill 完成再跑,避免 in_progress_session 状态被破坏
Integration
- 上游:SessionStart hook 检测
state.schema_version != LATEST_SCHEMA→ 输出红色警告 + 建议跑/cheat-migrate - 上游(手动):用户 git pull 拉了新版后,看 CHANGELOG 标 BREAKING → 主动跑
- 下游:跑完后所有其他 skill 读 state 都能拿到最新字段
- 与
cheat-init:init 写新 state 时直接用 LATEST_SCHEMA,不需要走 migrate - 与
install.sh --reinstall-hooks:迁移不重装 hook 脚本(hook 脚本属于 skill 包代码,不属于用户 state)。这两件事解耦
State 字段读写
本 skill 写:
schema_version(每步成功后更新)
本 skill 读:
- 所有现有字段(取决于具体 migration 文件的 HOW 步骤)
本 skill 绝不写:
calibration_samples/pending_retros/shoots等业务状态(这些是其他 skill 的职责)- 例外:迁移文件明确说"派生新字段值时需要扫 predictions/ 算 baseline_plays",那是初始化新字段,不是改老字段
Examples
示例 1:用户从 v0.1.0 升到 v0.2.0(假设 0.2 引入 schema 1.2)
用户:迁移
Claude: [跑 cheat-migrate]
Phase 0: current=1.1, target=1.2, chain=[(1.1, 1.2)]
Phase 1: dry-run 输出计划
用户: yes
Phase 2: 备份
Phase 3: 应用 1.1-to-1.2.md(MINOR:新增 platform_metrics_url 等字段)
Phase 4: 验证 OK
Phase 5: 报告 ✅
示例 2:用户跳了多版
用户:我从 v0.1.0 升到 v0.5.0,state 还是 1.0
Claude: [跑 cheat-migrate]
Phase 0: current=1.0, target=1.4 (LATEST), chain=[(1.0, 1.1), (1.1, 1.2), (1.2, 1.3), (1.3, 1.4)]
Phase 1: dry-run 输出 4 步计划
...
示例 3:迁移中途失败
Phase 3:
→ [1/4] 应用 1.0-to-1.1.md ✓
→ [2/4] 应用 1.1-to-1.2.md ✓
→ [3/4] 应用 1.2-to-1.3.md... ❌ 失败:用户 baseline_plays 字段含非数字值,无法转 int
state 已停在 schema_version: 1.2。
修复 .cheat-state.json 后重跑 /cheat-migrate 会从 1.2 → 1.3 继续。